Сравнение с R и библиотеками R

Поскольку pandas стремится предоставить функции обработки и анализа данных, сходные с теми, для которых используют R, мы создали эту страницу с более подробным описанием языка R и его многочисленных сторонних библиотек в сравнении с pandas. По сравнению с R и библиотеками CRAN, нас интересуют следующие вещи:

  • Функциональность и гибкость: что можно и нельзя делать с каждым инструментом.

  • Производительность: насколько быстро выполняются операции. Точные цифры и эталонные показатели предпочтительнее.

  • Простота использования: какой инструмент проще или сложнее в использовании (возможно, вам придется делать выводы на основе параллельного сравнения кода).

Эта страница также предлагает небольшое руководство по переводу понятий для пользователей R.

Одним из вариантов переноса объектов DataFrame из pandas в R является использование файлов HDF5, см. пример в разделе руководства пользователя о внешней совместимости.

Краткий справочник

Мы начнем с краткого справочного руководства, объединяющего некоторые распространенные операции R с использованием dplyr с эквивалентами pandas.

Запросы, фильтрация, выборка

R

pandas

dim(df)

df.shape

head(df)

df.head()

slice(df, 1:10)

df.iloc[:9]

filter(df, col1 == 1, col2 == 1)

df.query('col1 == 1 & col2 == 1')

df[df$col1 == 1 & df$col2 == 1,]

df[(df.col1 == 1) & (df.col2 == 1)]

select(df, col1, col2)

df[['col1', 'col2']]

select(df, col1:col3)

df.loc[:, 'col1':'col3']

select(df, -(col1:col3))

df.drop(cols_to_drop, axis=1) но см. 1

distinct(select(df, col1))

df[['col1']].drop_duplicates()

distinct(select(df, col1, col2))

df[['col1', 'col2']].drop_duplicates()

sample_n(df, 10)

df.sample(n=10)

sample_frac(df, 0.01)

df.sample(frac=0.01)

1

Сокращению R для поддиапазона столбцов (select(df, col1:col3)) можно легко подобрать аналог в pandas, если у вас есть список столбцов, например, df[cols[1:3]] или df.drop(cols[1:3]). Однако делать это по имени столбца немного неудобно.

Сортировка

R

pandas

arrange(df, col1, col2)

df.sort_values(['col1', 'col2'])

arrange(df, desc(col1))

df.sort_values('col1', ascending=False)

Преобразование

R

pandas

select(df, col_one = col1)

df.rename(columns={'col1': 'col_one'})['col_one']

rename(df, col_one = col1)

df.rename(columns={'col1': 'col_one'})

mutate(df, c=a-b)

df.assign(c=df['a']-df['b'])

Группировка и обобщение

R

pandas

summary(df)

df.describe()

gdf <- group_by(df, col1)

gdf = df.groupby('col1')

summarise(gdf, avg=mean(col1, na.rm=TRUE))

df.groupby('col1').agg({'col1': 'mean'})

summarise(gdf, total=sum(col1))

df.groupby('col1').sum()

Основы R

Срезы в R с помощью c

R упрощает доступ к столбцам data.frame по имени:

df <- data.frame(a=rnorm(5), b=rnorm(5), c=rnorm(5), d=rnorm(5), e=rnorm(5))
df[, c("a", "c", "e")]

или по целочисленному местоположению:

df <- data.frame(matrix(rnorm(1000), ncol=100))
df[, c(1:10, 25:30, 40, 50:100)]

Выбор нескольких столбцов по имени в pandas прост:

In [1]: df = pd.DataFrame(np.random.randn(10, 3), columns=list("abc"))

In [2]: df[["a", "c"]]
Out[2]: 
          a         c
0  0.469112 -1.509059
1 -1.135632 -0.173215
2  0.119209 -0.861849
3 -2.104569  1.071804
4  0.721555 -1.039575
5  0.271860  0.567020
6  0.276232 -0.673690
7  0.113648  0.524988
8  0.404705 -1.715002
9 -1.039268 -1.157892

In [3]: df.loc[:, ["a", "c"]]
Out[3]: 
          a         c
0  0.469112 -1.509059
1 -1.135632 -0.173215
2  0.119209 -0.861849
3 -2.104569  1.071804
4  0.721555 -1.039575
5  0.271860  0.567020
6  0.276232 -0.673690
7  0.113648  0.524988
8  0.404705 -1.715002
9 -1.039268 -1.157892

Выбрать несколько несмежных столбцов по целочисленному местоположению можно с помощью комбинации атрибута индексатора iloc и numpy.r_.

In [4]: named = list("abcdefg")

In [5]: n = 30

In [6]: columns = named + np.arange(len(named), n).tolist()

In [7]: df = pd.DataFrame(np.random.randn(n, n), columns=columns)

In [8]: df.iloc[:, np.r_[:10, 24:30]]
Out[8]: 
           a         b         c         d         e  ...        25        26        27        28        29
0  -1.344312  0.844885  1.075770 -0.109050  1.643563  ... -0.226169  0.410835  0.813850  0.132003 -0.827317
1  -0.076467 -1.187678  1.130127 -1.436737 -1.413681  ... -1.110336 -0.619976  0.149748 -0.732339  0.687738
2   0.176444  0.403310 -0.154951  0.301624 -2.179861  ...  0.432390  1.519970 -0.493662  0.600178  0.274230
3   0.132885 -0.023688  2.410179  1.450520  0.206053  ... -0.281461  0.030711  0.109121  1.126203 -0.977349
4   1.474071 -0.064034 -1.282782  0.781836 -1.071357  ... -1.066969 -0.303421 -0.858447  0.306996 -0.028665
..       ...       ...       ...       ...       ...  ...       ...       ...       ...       ...       ...
25  1.492125 -0.068190  0.681456  1.221829 -0.434352  ...  0.042344 -0.307904  0.428572  0.880609  0.487645
26  0.725238  0.624607 -0.141185 -0.143948 -0.328162  ...  1.190624  0.778507  1.008500  1.424017  0.717110
27  1.262419  1.950057  0.301038 -0.933858  0.814946  ...  0.334281 -0.162227  1.007824  2.826008  1.458383
28 -1.585746 -0.899734  0.921494 -0.211762 -0.059182  ... -0.026602 -0.240481  0.577223 -1.088417  0.326687
29 -0.986248  0.169729 -1.158091  1.019673  0.646039  ... -0.671466  0.332872 -2.013086 -1.602549  0.333109

[30 rows x 16 columns]

aggregate

В R вы можете разделить данные на подмножества и вычислить среднее значение для каждого из них. Использование data.frame с именем df и разделение его на группы by1 и by2:

df <- data.frame(
  v1 = c(1,3,5,7,8,3,5,NA,4,5,7,9),
  v2 = c(11,33,55,77,88,33,55,NA,44,55,77,99),
  by1 = c("red", "blue", 1, 2, NA, "big", 1, 2, "red", 1, NA, 12),
  by2 = c("wet", "dry", 99, 95, NA, "damp", 95, 99, "red", 99, NA, NA))
aggregate(x=df[, c("v1", "v2")], by=list(mydf2$by1, mydf2$by2), FUN = mean)

Метод pandas.DataFrame.groupby() аналогичен базовой функции R aggregate.

In [9]: df = pd.DataFrame(
   ...:     {
   ...:         "v1": [1, 3, 5, 7, 8, 3, 5, np.nan, 4, 5, 7, 9],
   ...:         "v2": [11, 33, 55, 77, 88, 33, 55, np.nan, 44, 55, 77, 99],
   ...:         "by1": ["red", "blue", 1, 2, np.nan, "big", 1, 2, "red", 1, np.nan, 12],
   ...:         "by2": [
   ...:             "wet",
   ...:             "dry",
   ...:             99,
   ...:             95,
   ...:             np.nan,
   ...:             "damp",
   ...:             95,
   ...:             99,
   ...:             "red",
   ...:             99,
   ...:             np.nan,
   ...:             np.nan,
   ...:         ],
   ...:     }
   ...: )
   ...: 

In [10]: g = df.groupby(["by1", "by2"])

In [11]: g[["v1", "v2"]].mean()
Out[11]: 
            v1    v2
by1  by2            
1    95    5.0  55.0
     99    5.0  55.0
2    95    7.0  77.0
     99    NaN   NaN
big  damp  3.0  33.0
blue dry   3.0  33.0
red  red   4.0  44.0
     wet   1.0  11.0

Дополнительные сведения и примеры см. в разделе руководства пользователя о группировании.

match / %in%

Распространенным способом выбора данных в R является использование %in%, который определяется с помощью функции match. Оператор %in% используется для возврата логического вектора, указывающего, есть совпадение или нет:

s <- 0:4
s %in% c(2,4)

Метод pandas.DataFrame.isin() аналогичен оператору R %in%:

In [12]: s = pd.Series(np.arange(5), dtype=np.float32)

In [13]: s.isin([2, 4])
Out[13]: 
0    False
1    False
2     True
3    False
4     True
dtype: bool

Функция match возвращает вектор, в котором отображены совпадения первого аргумента со вторым:

s <- 0:4
match(s, c(2,4))

Более подробную информацию и примеры см. в разделе руководства пользователя об индексировании.

tapply

tapply похож на aggregate, но данные могут быть в неравномерном массиве, поскольку размеры подклассов могут быть неравномерными. Использование data.frame под названием baseball и получение информации на основе массива team:

baseball <-
  data.frame(team = gl(5, 5,
             labels = paste("Team", LETTERS[1:5])),
             player = sample(letters, 25),
             batting.average = runif(25, .200, .400))

tapply(baseball$batting.average, baseball.example$team,
       max)

В pandas мы можем использовать метод pandas.pivot_table(), чтобы решить аналогичную задачу:

In [14]: import random

In [15]: import string

In [16]: baseball = pd.DataFrame(
   ....:     {
   ....:         "team": ["team %d" % (x + 1) for x in range(5)] * 5,
   ....:         "player": random.sample(list(string.ascii_lowercase), 25),
   ....:         "batting avg": np.random.uniform(0.200, 0.400, 25),
   ....:     }
   ....: )
   ....: 

In [17]: baseball.pivot_table(values="batting avg", columns="team", aggfunc=np.max)
Out[17]: 
team           team 1    team 2    team 3    team 4    team 5
batting avg  0.352134  0.295327  0.397191  0.394457  0.396194

Дополнительные сведения и примеры см. в разделе руководства пользователя о сводных таблицах.

subset

Метод pandas.DataFrame.query() похож на базовую функцию R subset. В R можно получить строки data.frame, где значения одного столбца меньше, чем значения другого:

df <- data.frame(a=rnorm(10), b=rnorm(10))
subset(df, a <= b)
df[df$a <= df$b,]  # note the comma

В pandas есть несколько способов выбрать подмножество. Вы можете использовать query() или передать выражение, как если бы это был индекс или срез, а также стандартное логическое индексирование:

In [18]: df = pd.DataFrame({"a": np.random.randn(10), "b": np.random.randn(10)})

In [19]: df.query("a <= b")
Out[19]: 
          a         b
1  0.174950  0.552887
2 -0.023167  0.148084
3 -0.495291 -0.300218
4 -0.860736  0.197378
5 -1.134146  1.720780
7 -0.290098  0.083515
8  0.238636  0.946550

In [20]: df[df["a"] <= df["b"]]
Out[20]: 
          a         b
1  0.174950  0.552887
2 -0.023167  0.148084
3 -0.495291 -0.300218
4 -0.860736  0.197378
5 -1.134146  1.720780
7 -0.290098  0.083515
8  0.238636  0.946550

In [21]: df.loc[df["a"] <= df["b"]]
Out[21]: 
          a         b
1  0.174950  0.552887
2 -0.023167  0.148084
3 -0.495291 -0.300218
4 -0.860736  0.197378
5 -1.134146  1.720780
7 -0.290098  0.083515
8  0.238636  0.946550

Дополнительные сведения и примеры см. в разделе о запросах.

with

В R выражение для data.frame с именем df и столбцами a и b оценивается с использованием with следующим образом:

df <- data.frame(a=rnorm(10), b=rnorm(10))
with(df, a + b)
df$a + df$b  # same as the previous expression

В pandas эквивалентное выражение с использованием метода pandas.DataFrame.eval() будет выглядеть так:

In [22]: df = pd.DataFrame({"a": np.random.randn(10), "b": np.random.randn(10)})

In [23]: df.eval("a + b")
Out[23]: 
0   -0.091430
1   -2.483890
2   -0.252728
3   -0.626444
4   -0.261740
5    2.149503
6   -0.332214
7    0.799331
8   -2.377245
9    2.104677
dtype: float64

In [24]: df["a"] + df["b"]  # same as the previous expression
Out[24]: 
0   -0.091430
1   -2.483890
2   -0.252728
3   -0.626444
4   -0.261740
5    2.149503
6   -0.332214
7    0.799331
8   -2.377245
9    2.104677
dtype: float64

В некоторых случаях eval() намного быстрее, чем оценка в чистом Python. Дополнительные сведения и примеры см. в документации по eval.

plyr

plyr — это библиотека R, реализующая подход «разделить-применить-объединить» в анализе данных. Функции R вращаются вокруг трех структур данных: a для arrays, l для lists и d для data.frame. В таблице ниже показано, как эти структуры данных могут быть отображены в Python.

R

Python

array

list

lists

dictionary или list of objects

data.frame

dataframe

ddply

Выражение в R, использующее data.frame с именем df, где суммируются x по month:

require(plyr)
df <- data.frame(
  x = runif(120, 1, 168),
  y = runif(120, 7, 334),
  z = runif(120, 1.7, 20.7),
  month = rep(c(5,6,7,8),30),
  week = sample(1:4, 120, TRUE)
)

ddply(df, .(month, week), summarize,
      mean = round(mean(x), 2),
      sd = round(sd(x), 2))

В pandas эквивалентное выражение с использованием метода groupby() будет выглядеть так:

In [25]: df = pd.DataFrame(
   ....:     {
   ....:         "x": np.random.uniform(1.0, 168.0, 120),
   ....:         "y": np.random.uniform(7.0, 334.0, 120),
   ....:         "z": np.random.uniform(1.7, 20.7, 120),
   ....:         "month": [5, 6, 7, 8] * 30,
   ....:         "week": np.random.randint(1, 4, 120),
   ....:     }
   ....: )
   ....: 

In [26]: grouped = df.groupby(["month", "week"])

In [27]: grouped["x"].agg([np.mean, np.std])
Out[27]: 
                  mean        std
month week                       
5     1      63.653367  40.601965
      2      78.126605  53.342400
      3      92.091886  57.630110
6     1      81.747070  54.339218
      2      70.971205  54.687287
      3     100.968344  54.010081
7     1      61.576332  38.844274
      2      61.733510  48.209013
      3      71.688795  37.595638
8     1      62.741922  34.618153
      2      91.774627  49.790202
      3      73.936856  60.773900

Дополнительные сведения и примеры см. в документации по groupby.

reshape и reshape2

meltarray

Выражение в R, использующее трехмерный массив с именем a, который вы хотите преобразовать в data.frame:

a <- array(c(1:23, NA), c(2,3,4))
data.frame(melt(a))

В Python, поскольку a — это список, вы можете просто использовать списковое включение.

In [28]: a = np.array(list(range(1, 24)) + [np.NAN]).reshape(2, 3, 4)

In [29]: pd.DataFrame([tuple(list(x) + [val]) for x, val in np.ndenumerate(a)])
Out[29]: 
    0  1  2     3
0   0  0  0   1.0
1   0  0  1   2.0
2   0  0  2   3.0
3   0  0  3   4.0
4   0  1  0   5.0
.. .. .. ..   ...
19  1  1  3  20.0
20  1  2  0  21.0
21  1  2  1  22.0
22  1  2  2  23.0
23  1  2  3   NaN

[24 rows x 4 columns]

meltlist

Выражение в R, использующее список с именем a, который вы хотите преобразовать в data.frame:

a <- as.list(c(1:4, NA))
data.frame(melt(a))

В Python этот список будет списком кортежей, поэтому метод DataFrame() будет преобразовывать список в dataframe.

In [30]: a = list(enumerate(list(range(1, 5)) + [np.NAN]))

In [31]: pd.DataFrame(a)
Out[31]: 
   0    1
0  0  1.0
1  1  2.0
2  2  3.0
3  3  4.0
4  4  NaN

Дополнительные сведения и примеры см. во введении в структуры данных.

meltdf

Выражение, использующее data.frame под названием cheese в R, где вы хотите изменить форму data.frame:

cheese <- data.frame(
  first = c('John', 'Mary'),
  last = c('Doe', 'Bo'),
  height = c(5.5, 6.0),
  weight = c(130, 150)
)
melt(cheese, id=c("first", "last"))

Эквивалентом этого метода в Python является метод melt():

In [32]: cheese = pd.DataFrame(
   ....:     {
   ....:         "first": ["John", "Mary"],
   ....:         "last": ["Doe", "Bo"],
   ....:         "height": [5.5, 6.0],
   ....:         "weight": [130, 150],
   ....:     }
   ....: )
   ....: 

In [33]: pd.melt(cheese, id_vars=["first", "last"])
Out[33]: 
  first last variable  value
0  John  Doe   height    5.5
1  Mary   Bo   height    6.0
2  John  Doe   weight  130.0
3  Mary   Bo   weight  150.0

In [34]: cheese.set_index(["first", "last"]).stack()  # alternative way
Out[34]: 
first  last        
John   Doe   height      5.5
             weight    130.0
Mary   Bo    height      6.0
             weight    150.0
dtype: float64

Дополнительные сведения и примеры см. в документации по изменению формы.

cast

В R acast — это выражение, использующее data.frame с именем df для приведения к массиву более высокой мерности:

df <- data.frame(
  x = runif(12, 1, 168),
  y = runif(12, 7, 334),
  z = runif(12, 1.7, 20.7),
  month = rep(c(5,6,7),4),
  week = rep(c(1,2), 6)
)

mdf <- melt(df, id=c("month", "week"))
acast(mdf, week ~ month ~ variable, mean)

В Python лучше всего использовать pivot_table():

In [35]: df = pd.DataFrame(
   ....:     {
   ....:         "x": np.random.uniform(1.0, 168.0, 12),
   ....:         "y": np.random.uniform(7.0, 334.0, 12),
   ....:         "z": np.random.uniform(1.7, 20.7, 12),
   ....:         "month": [5, 6, 7] * 4,
   ....:         "week": [1, 2] * 6,
   ....:     }
   ....: )
   ....: 

In [36]: mdf = pd.melt(df, id_vars=["month", "week"])

In [37]: pd.pivot_table(
   ....:     mdf,
   ....:     values="value",
   ....:     index=["variable", "week"],
   ....:     columns=["month"],
   ....:     aggfunc=np.mean,
   ....: )
   ....: 
Out[37]: 
month                  5           6           7
variable week                                   
x        1     93.888747   98.762034   55.219673
         2     94.391427   38.112932   83.942781
y        1     94.306912  279.454811  227.840449
         2     87.392662  193.028166  173.899260
z        1     11.016009   10.079307   16.170549
         2      8.476111   17.638509   19.003494

Аналогично для dcast, который в R использует data.frame с именем df для агрегирования информации на основе Animal и FeedType:

df <- data.frame(
  Animal = c('Animal1', 'Animal2', 'Animal3', 'Animal2', 'Animal1',
             'Animal2', 'Animal3'),
  FeedType = c('A', 'B', 'A', 'A', 'B', 'B', 'A'),
  Amount = c(10, 7, 4, 2, 5, 6, 2)
)

dcast(df, Animal ~ FeedType, sum, fill=NaN)
# Alternative method using base R
with(df, tapply(Amount, list(Animal, FeedType), sum))

В Python можно подойти к этому двумя разными способами. Во-первых, как и выше, используя pivot_table():

In [38]: df = pd.DataFrame(
   ....:     {
   ....:         "Animal": [
   ....:             "Animal1",
   ....:             "Animal2",
   ....:             "Animal3",
   ....:             "Animal2",
   ....:             "Animal1",
   ....:             "Animal2",
   ....:             "Animal3",
   ....:         ],
   ....:         "FeedType": ["A", "B", "A", "A", "B", "B", "A"],
   ....:         "Amount": [10, 7, 4, 2, 5, 6, 2],
   ....:     }
   ....: )
   ....: 

In [39]: df.pivot_table(values="Amount", index="Animal", columns="FeedType", aggfunc="sum")
Out[39]: 
FeedType     A     B
Animal              
Animal1   10.0   5.0
Animal2    2.0  13.0
Animal3    6.0   NaN

Второй подход заключается в использовании метода groupby():

In [40]: df.groupby(["Animal", "FeedType"])["Amount"].sum()
Out[40]: 
Animal   FeedType
Animal1  A           10
         B            5
Animal2  A            2
         B           13
Animal3  A            6
Name: Amount, dtype: int64

Для получения дополнительной информации и примеров см. документацию по изменению формы или документацию по groupby.

factor

В R есть тип данных для категориальных данных.

cut(c(1,2,3,4,5,6), 3)
factor(c(1,2,3,2,2,3))

В pandas это достигается с помощью pd.cut и astype("category"):

In [41]: pd.cut(pd.Series([1, 2, 3, 4, 5, 6]), 3)
Out[41]: 
0    (0.995, 2.667]
1    (0.995, 2.667]
2    (2.667, 4.333]
3    (2.667, 4.333]
4      (4.333, 6.0]
5      (4.333, 6.0]
dtype: category
Categories (3, interval[float64, right]): [(0.995, 2.667] < (2.667, 4.333] < (4.333, 6.0]]

In [42]: pd.Series([1, 2, 3, 2, 2, 3]).astype("category")
Out[42]: 
0    1
1    2
2    3
3    2
4    2
5    3
dtype: category
Categories (3, int64): [1, 2, 3]

Дополнительные сведения и примеры см. во введении в категориальные данные и в документации по API. Существует также документация, касающаяся отличий от factor в R.