Spec-Zone.ru › pandas 0.19

Основные базовые функции

Здесь мы обсудим множество основных функций, общих для структур данных pandas. Вот как создать некоторые объекты, используемые в примерах из предыдущего раздела:

In [1]: index = pd.date_range('1/1/2000', periods=8)

In [2]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index,
   ...:                   columns=['A', 'B', 'C'])
   ...: 

In [4]: wp = pd.Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'],
   ...:               major_axis=pd.date_range('1/1/2000', periods=5),
   ...:               minor_axis=['A', 'B', 'C', 'D'])
   ...: 

Заголовки и хвосты

Чтобы просмотреть небольшую выборку объекта Series или DataFrame, используйте методы head() и tail(). По умолчанию отображается пять элементов, но вы можете указать любое количество.

In [5]: long_series = pd.Series(np.random.randn(1000))

In [6]: long_series.head()
Out[6]: 
0   -0.305384
1   -0.479195
2    0.095031
3   -0.270099
4   -0.707140
dtype: float64

In [7]: long_series.tail(3)
Out[7]: 
997    0.588446
998    0.026465
999   -1.728222
dtype: float64

Атрибуты и исходный массив ndarray

Объекты pandas имеют ряд атрибутов, позволяющих получить доступ к метаданным

  • shape: задаёт размерность осей объекта, согласуясь с ndarray
  • Метки осей
    • Series: index (только ось)
    • DataFrame: index (строки) и columns (колонки)
    • Panel: items, major_axis и minor_axis

Обратите внимание, что эти атрибуты можно безопасно назначить!

In [8]: df[:2]
Out[8]: 
                   A         B         C
2000-01-01  0.187483 -1.933946  0.377312
2000-01-02  0.734122  2.141616 -0.011225

In [9]: df.columns = [x.lower() for x in df.columns]

In [10]: df
Out[10]: 
                   a         b         c
2000-01-01  0.187483 -1.933946  0.377312
2000-01-02  0.734122  2.141616 -0.011225
2000-01-03  0.048869 -1.360687 -0.479010
2000-01-04 -0.859661 -0.231595 -0.527750
2000-01-05 -1.296337  0.150680  0.123836
2000-01-06  0.571764  1.555563 -0.823761
2000-01-07  0.535420 -1.032853  1.469725
2000-01-08  1.304124  1.449735  0.203109

Чтобы получить фактические данные внутри структуры данных, достаточно обратиться к свойству values:

In [11]: s.values
Out[11]: array([ 0.1122,  0.8717, -0.8161, -0.7849,  1.0307])

In [12]: df.values
Out[12]: 
array([[ 0.1875, -1.9339,  0.3773],
       [ 0.7341,  2.1416, -0.0112],
       [ 0.0489, -1.3607, -0.479 ],
       [-0.8597, -0.2316, -0.5278],
       [-1.2963,  0.1507,  0.1238],
       [ 0.5718,  1.5556, -0.8238],
       [ 0.5354, -1.0329,  1.4697],
       [ 1.3041,  1.4497,  0.2031]])

In [13]: wp.values
Out[13]: 
array([[[-1.032 ,  0.9698, -0.9627,  1.3821],
        [-0.9388,  0.6691, -0.4336, -0.2736],
        [ 0.6804, -0.3084, -0.2761, -1.8212],
        [-1.9936, -1.9274, -2.0279,  1.625 ],
        [ 0.5511,  3.0593,  0.4553, -0.0307]],

       [[ 0.9357,  1.0612, -2.1079,  0.1999],
        [ 0.3236, -0.6416, -0.5875,  0.0539],
        [ 0.1949, -0.382 ,  0.3186,  2.0891],
        [-0.7283, -0.0903, -0.7482,  1.3189],
        [-2.0298,  0.7927,  0.461 , -0.5427]]])

Если DataFrame или Panel содержат данные с однородным типом, массив ndarray можно фактически изменить на месте, и изменения будут отражены в структуре данных. Для разнородных данных (например, некоторые столбцы DataFrame не имеют одного и того же типа данных), это не будет так. В отличие от меток осей, свойству values нельзя присвоить значение.

Примечание

При работе с разнородными данными тип данных результирующего массива ndarray будет выбран для обеспечения совместимости со всеми вовлечёнными данными. Например, если используются строки, результат будет иметь тип object. Если присутствуют только числа с плавающей точкой и целые числа, результирующий массив будет иметь тип float.

Ускоренные операции

pandas поддерживает ускорение некоторых типов бинарных числовых и булевых операций, используя библиотеку numexpr (начиная с версии 0.11.0) и библиотеки bottleneck.

Эти библиотеки особенно полезны при работе с большими наборами данных и обеспечивают значительное ускорение. numexpr использует разумное дробление, кэширование и несколько ядер. bottleneck представляет собой набор специализированных цитоновых процедур, которые особенно быстры при работе с массивами, имеющими nans.

Вот пример (используя 100 столбцов и 100 000 строк DataFrames):

Операция 0.11.0 (мс) Предыдущая версия (мс) Отношение к предыдущей
df1 > df2 13.32 125.35 0.1063
df1 * df2 21.71 36.63 0.5928
df1 + df2 22.04 36.50 0.6039

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

Гибкие бинарные операции

При бинарных операциях между структурами данных pandas есть две ключевые особенности:

  • Поведение векторизации между объектами более высокого (например, DataFrame) и более низкого (например, Series) измерения.
  • Отсутствующие данные в вычислениях

Мы продемонстрируем, как управлять этими проблемами независимо, хотя их можно обрабатывать одновременно.

Соответствие/векторизация

DataFrame имеет методы add(), sub(), mul(), div() и соответствующие функции radd(), rsub(), ... для выполнения бинарных операций. Для поведения векторизации в качестве входных данных в первую очередь рассматривается Series. С помощью этих функций вы можете соответствовать меткам index или columns через ключевое слово axis:

In [14]: df = pd.DataFrame({'one' : pd.Series(np.random.randn(3), index=['a', 'b', 'c']),
   ....:                    'two' : pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']),
   ....:                    'three' : pd.Series(np.random.randn(3), index=['b', 'c', 'd'])})
   ....: 

In [15]: df
Out[15]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789
d       NaN  1.124472 -1.101558

In [16]: row = df.ix[1]

In [17]: column = df['two']

In [18]: df.sub(row, axis='columns')
Out[18]: 
        one     three       two
a -0.487650       NaN -1.487837
b  0.000000  0.000000  0.000000
c  0.150512  0.639504 -1.585038
d       NaN  1.301762 -2.237808

In [19]: df.sub(row, axis=1)
Out[19]: 
        one     three       two
a -0.487650       NaN -1.487837
b  0.000000  0.000000  0.000000
c  0.150512  0.639504 -1.585038
d       NaN  1.301762 -2.237808

In [20]: df.sub(column, axis='index')
Out[20]: 
        one     three  two
a -0.274957       NaN  0.0
b -1.275144 -1.313539  0.0
c  0.460406  0.911003  0.0
d       NaN  2.226031  0.0

In [21]: df.sub(column, axis=0)
Out[21]: 
        one     three  two
a -0.274957       NaN  0.0
b -1.275144 -1.313539  0.0
c  0.460406  0.911003  0.0
d       NaN  2.226031  0.0

Кроме того, вы можете выровнять уровень многоиндексированного DataFrame с Series.

In [22]: dfmi = df.copy()

In [23]: dfmi.index = pd.MultiIndex.from_tuples([(1,'a'),(1,'b'),(1,'c'),(2,'a')],
   ....:                                        names=['first','second'])
   ....: 

In [24]: dfmi.sub(column, axis=0, level='second')
Out[24]: 
                   one     three       two
first second                              
1     a      -0.274957       NaN  0.000000
      b      -1.275144 -1.313539  0.000000
      c       0.460406  0.911003  0.000000
2     a            NaN  1.476060 -0.749971

С Panel описание поведения векторизации немного сложнее, поэтому методы арифметических операций (и, возможно, запутанно?) дают вам возможность указать векторную ось. Например, предположим, что мы хотим вычесть среднее значение из данных по определённой оси. Это можно сделать, вычислив среднее значение по оси и выполнив векторизацию по этой же оси:

In [25]: major_mean = wp.mean(axis='major')

In [26]: major_mean
Out[26]: 
      Item1     Item2
A -0.546569 -0.260774
B  0.492478  0.147993
C -0.649010 -0.532794
D  0.176307  0.623812

In [27]: wp.sub(major_mean, axis='major')
Out[27]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: Item1 to Item2
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
Minor_axis axis: A to D

И аналогично для axis="items" и axis="minor".

Примечание

Я мог бы согласиться на изменение аргумента axis в методах DataFrame для соответствия поведению векторизации в Panel. Хотя это потребует переходного периода, чтобы пользователи могли изменить свой код...

Series и Index также поддерживают встроенную функцию divmod(). Эта функция выполняет целочисленное деление и нахождение остатка одновременно, возвращая кортеж с типом, соответствующим левой части выражения. Например:

In [28]: s = pd.Series(np.arange(10))

In [29]: s
Out[29]: 
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int64

In [30]: div, rem = divmod(s, 3)

In [31]: div
Out[31]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    2
7    2
8    2
9    3
dtype: int64

In [32]: rem
Out[32]: 
0    0
1    1
2    2
3    0
4    1
5    2
6    0
7    1
8    2
9    0
dtype: int64

In [33]: idx = pd.Index(np.arange(10))

In [34]: idx
Out[34]: Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')

In [35]: div, rem = divmod(idx, 3)

In [36]: div
Out[36]: Int64Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')

In [37]: rem
Out[37]: Int64Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')

Мы также можем выполнить поэлементное divmod():

In [38]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])

In [39]: div
Out[39]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    1
7    1
8    1
9    1
dtype: int64

In [40]: rem
Out[40]: 
0    0
1    1
2    2
3    0
4    0
5    1
6    1
7    2
8    2
9    3
dtype: int64

Отсутствующие данные/операции со значениями заполнения

В Series и DataFrame (но ещё не в Panel) арифметические функции имеют возможность ввода fill_value, то есть значения для подстановки, когда отсутствует максимум одно значение в определённой позиции. Например, при сложении двух объектов DataFrame вы можете захотеть рассматривать NaN как 0, если оба DataFrame пропускают это значение, в противном случае результат будет NaN (позже вы можете заменить NaN другим значением с помощью fillna если хотите).

In [41]: df
Out[41]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789
d       NaN  1.124472 -1.101558

In [42]: df2
Out[42]: 
        one     three       two
a -0.626544  1.000000 -0.351587
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789
d       NaN  1.124472 -1.101558

In [43]: df + df2
Out[43]: 
        one     three       two
a -1.253088       NaN -0.703174
b -0.277789 -0.354579  2.272499
c  0.023235  0.924429 -0.897577
d       NaN  2.248945 -2.203116

In [44]: df.add(df2, fill_value=0)
Out[44]: 
        one     three       two
a -1.253088  1.000000 -0.703174
b -0.277789 -0.354579  2.272499
c  0.023235  0.924429 -0.897577
d       NaN  2.248945 -2.203116

Гибкие сравнения

Начиная с версии 0.8, pandas ввёл бинарные методы сравнения eq, ne, lt, gt, le и ge для Series и DataFrame, поведение которых аналогично описанным выше бинарным арифметическим операциям:

In [45]: df.gt(df2)
Out[45]: 
     one  three    two
a  False  False  False
b  False  False  False
c  False  False  False
d  False  False  False

In [46]: df2.ne(df)
Out[46]: 
     one  three    two
a  False   True  False
b  False  False  False
c  False  False  False
d   True  False  False

Эти операции создают объект pandas того же типа, что и входной объект слева, если тип данных bool. Эти boolean объекты могут быть использованы в операциях индексирования, см. здесь

Булевы редукции

Вы можете применить редукции: empty, any(), all() и bool(), чтобы получить способ обобщения булевого результата.

In [47]: (df > 0).all()
Out[47]: 
one      False
three    False
two      False
dtype: bool

In [48]: (df > 0).any()
Out[48]: 
one      True
three    True
two      True
dtype: bool

Вы можете свести результат к окончательному булевому значению.

In [49]: (df > 0).any().any()
Out[49]: True

Вы можете проверить, пуст ли объект pandas, с помощью свойства empty.

In [50]: df.empty
Out[50]: False

In [51]: pd.DataFrame(columns=list('ABC')).empty
Out[51]: True

Для оценки булевого значения одного элемента в объекте pandas используйте метод bool():

In [52]: pd.Series([True]).bool()
Out[52]: True

In [53]: pd.Series([False]).bool()
Out[53]: False

In [54]: pd.DataFrame([[True]]).bool()
Out[54]: True

In [55]: pd.DataFrame([[False]]).bool()
Out[55]: False

Предупреждение

Вы можете быть искушены сделать следующее:

>>> if df:
     ...

Или

>>> df and df2

Оба варианта приведут к ошибке, так как вы пытаетесь сравнить несколько значений.

ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().

См. подводные камни для более подробного обсуждения.

Сравнение объектов на эквивалентность

Часто вы можете найти более одного способа вычисления одного и того же результата. Например, рассмотрим df+df и df*2. Чтобы проверить, эти два вычисления дают один и тот же результат, используя инструменты, показанные выше, вы можете предположить использование (df+df == df*2).all(). Но на самом деле это выражение ложно:

In [56]: df+df == df*2
Out[56]: 
     one  three   two
a   True  False  True
b   True   True  True
c   True   True  True
d  False   True  True

In [57]: (df+df == df*2).all()
Out[57]: 
one      False
three    False
two       True
dtype: bool

Обратите внимание, что булевый DataFrame df+df == df*2 содержит некоторые ложные значения! Это связано с тем, что NaN не сравниваются как равные:

In [58]: np.nan == np.nan
Out[58]: False

Таким образом, начиная с версии 0.13.1, NDFrames (такие как Series, DataFrame и Panel) имеют метод equals() для проверки на равенство, где NaN в соответствующих позициях рассматриваются как равные.

In [59]: (df+df).equals(df*2)
Out[59]: True

Обратите внимание, что индекс Series или DataFrame должен быть в том же порядке, чтобы равенство было истинным:

In [60]: df1 = pd.DataFrame({'col':['foo', 0, np.nan]})

In [61]: df2 = pd.DataFrame({'col':[np.nan, 0, 'foo']}, index=[2,1,0])

In [62]: df1.equals(df2)
Out[62]: False

In [63]: df1.equals(df2.sort_index())
Out[63]: True

Сравнение массивоподобных объектов

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

In [64]: pd.Series(['foo', 'bar', 'baz']) == 'foo'
Out[64]: 
0     True
1    False
2    False
dtype: bool

In [65]: pd.Index(['foo', 'bar', 'baz']) == 'foo'
Out[65]: array([ True, False, False], dtype=bool)

Pandas также обрабатывает поэлементные сравнения между различными массивоподобными объектами одинаковой длины:

In [66]: pd.Series(['foo', 'bar', 'baz']) == pd.Index(['foo', 'bar', 'qux'])
Out[66]: 
0     True
1     True
2    False
dtype: bool

In [67]: pd.Series(['foo', 'bar', 'baz']) == np.array(['foo', 'bar', 'qux'])
Out[67]: 
0     True
1     True
2    False
dtype: bool

Попытка сравнения Index или Series объектов различной длины вызовет ошибку ValueError:

In [55]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
ValueError: Series lengths must match to compare

In [56]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
ValueError: Series lengths must match to compare

Обратите внимание, что это отличается от поведения numpy, где сравнение может быть векторизировано:

In [68]: np.array([1, 2, 3]) == np.array([2])
Out[68]: array([False,  True, False], dtype=bool)

или может вернуть False, если векторизация невозможна:

In [69]: np.array([1, 2, 3]) == np.array([1, 2])
Out[69]: False

Объединение перекрывающихся наборов данных

Иногда возникает проблема объединения двух похожих наборов данных, где значения одного предпочтительнее другого. Например, две временные ряды, представляющие определённый экономический показатель, где одна из них считается «более качественной». Однако, менее качественная временная ряд может охватывать более ранний период или иметь более полные данные. Поэтому мы хотим объединить два объекта DataFrame, где отсутствующие значения в одном DataFrame условно заполняются подобными значениями из другого DataFrame. Функция, реализующая эту операцию, — combine_first(), что мы иллюстрируем:

In [70]: df1 = pd.DataFrame({'A' : [1., np.nan, 3., 5., np.nan],
   ....:                     'B' : [np.nan, 2., 3., np.nan, 6.]})
   ....: 

In [71]: df2 = pd.DataFrame({'A' : [5., 2., 4., np.nan, 3., 7.],
   ....:                     'B' : [np.nan, np.nan, 3., 4., 6., 8.]})
   ....: 

In [72]: df1
Out[72]: 
     A    B
0  1.0  NaN
1  NaN  2.0
2  3.0  3.0
3  5.0  NaN
4  NaN  6.0

In [73]: df2
Out[73]: 
     A    B
0  5.0  NaN
1  2.0  NaN
2  4.0  3.0
3  NaN  4.0
4  3.0  6.0
5  7.0  8.0

In [74]: df1.combine_first(df2)
Out[74]: 
     A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0

Общее объединение DataFrame

combine_first() метод, описанный выше, вызывает более общий метод DataFrame combine(). Этот метод принимает другой DataFrame и функцию объединения, выравнивает входной DataFrame, а затем передает функцию объединения парам Series (т.е., столбцам, имеющим одинаковые имена).

Например, чтобы воспроизвести combine_first(), как показано выше:

In [75]: combiner = lambda x, y: np.where(pd.isnull(x), y, x)

In [76]: df1.combine(df2, combiner)
Out[76]: 
     A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0

Дескриптивные статистики

Большое количество методов для вычисления дескриптивных статистик и других связанных операций с Series, DataFrame и Panel. Большинство из них являются агрегациями (следовательно, производят результат с меньшей размерностью), например, sum(), mean() и quantile(), но некоторые из них, такие как cumsum() и cumprod(), производят объект той же размерности. Как правило, эти методы принимают аргумент axis, точно так же, как ndarray.{sum, std, ...}, но ось можно указать по имени или целочисленно:

  • Series: аргумент axis не требуется
  • DataFrame: “index” (axis=0, по умолчанию), “columns” (axis=1)
  • Panel: “items” (axis=0), “major” (axis=1, по умолчанию), “minor” (axis=2)

Например:

In [77]: df
Out[77]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789
d       NaN  1.124472 -1.101558

In [78]: df.mean(0)
Out[78]: 
one     -0.251274
three    0.469799
two     -0.191421
dtype: float64

In [79]: df.mean(1)
Out[79]: 
a   -0.489066
b    0.273355
c    0.008348
d    0.011457
dtype: float64

Все такие методы имеют опцию skipna , сигнализирующую о том, нужно ли исключить пропущенные данные (True по умолчанию):

In [80]: df.sum(0, skipna=False)
Out[80]: 
one           NaN
three         NaN
two     -0.765684
dtype: float64

In [81]: df.sum(axis=1, skipna=True)
Out[81]: 
a   -0.978131
b    0.820066
c    0.025044
d    0.022914
dtype: float64

В сочетании с поведением широковещательной передачи/арифметики можно очень лаконично описать различные статистические процедуры, такие как стандартизация (приведение данных к нулевому среднему и стандартному отклонению 1):

In [82]: ts_stand = (df - df.mean()) / df.std()

In [83]: ts_stand.std()
Out[83]: 
one      1.0
three    1.0
two      1.0
dtype: float64

In [84]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)

In [85]: xs_stand.std(1)
Out[85]: 
a    1.0
b    1.0
c    1.0
d    1.0
dtype: float64

Обратите внимание, что такие методы, как cumsum() и cumprod() сохраняют положение значений NA:

In [86]: df.cumsum()
Out[86]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.765438 -0.177289  0.784662
c -0.753821  0.284925  0.335874
d       NaN  1.409398 -0.765684

Вот сводная таблица быстрого справочника по общим функциям. Каждый из них также принимает необязательный параметр level , который применяется только в том случае, если объект имеет многоуровневый индекс.

Функция Описание
count Количество ненулевых наблюдений
sum Сумма значений
mean Среднее значение значений
mad Среднее абсолютное отклонение
median Арифметическая медиана значений
min Минимальное значение
max Максимальное значение
mode Мода
abs Модуль (абсолютное значение)
prod Произведение значений
std Исправленное по Бесселю выборочное стандартное отклонение
var Несмещенная дисперсия
sem Стандартная ошибка среднего
skew Выборовое асимметричное (3-й момент)
kurt Выборовый эксцесс (4-й момент)
quantile Выборовый квантиль (%)
cumsum Кумулятивная сумма
cumprod Кумулятивное произведение
cummax Накопленный максимум
cummin Накопленный минимум

Обратите внимание, что по случайности некоторые методы NumPy, такие как mean, std, и sum, по умолчанию будут исключать NA при вводе Series:

In [87]: np.mean(df['one'])
Out[87]: -0.2512736517583951

In [88]: np.mean(df['one'].values)
Out[88]: nan

Series также имеет метод nunique(), который возвращает количество уникальных ненулевых значений:

In [89]: series = pd.Series(np.random.randn(500))

In [90]: series[20:500] = np.nan

In [91]: series[10:20]  = 5

In [92]: series.nunique()
Out[92]: 11

Обобщение данных: describe

Существует удобная функция describe(), которая вычисляет различные сводные статистические данные о Series или столбцах DataFrame (исключая, разумеется, NA):

In [93]: series = pd.Series(np.random.randn(1000))

In [94]: series[::2] = np.nan

In [95]: series.describe()
Out[95]: 
count    500.000000
mean      -0.039663
std        1.069371
min       -3.463789
25%       -0.731101
50%       -0.058918
75%        0.672758
max        3.120271
dtype: float64

In [96]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=['a', 'b', 'c', 'd', 'e'])

In [97]: frame.ix[::2] = np.nan

In [98]: frame.describe()
Out[98]: 
                a           b           c           d           e
count  500.000000  500.000000  500.000000  500.000000  500.000000
mean     0.000954   -0.044014    0.075936   -0.003679    0.020751
std      1.005133    0.974882    0.967432    1.004732    0.963812
min     -3.010899   -2.782760   -3.401252   -2.944925   -3.794127
25%     -0.682900   -0.681161   -0.528190   -0.663503   -0.615717
50%     -0.001651   -0.006279    0.040098   -0.003378    0.006282
75%      0.656439    0.632852    0.717919    0.687214    0.653423
max      3.007143    2.627688    2.702490    2.850852    3.072117

Вы можете выбрать определенные процентили для включения в вывод:

In [99]: series.describe(percentiles=[.05, .25, .75, .95])
Out[99]: 
count    500.000000
mean      -0.039663
std        1.069371
min       -3.463789
5%        -1.741334
25%       -0.731101
50%       -0.058918
75%        0.672758
95%        1.854383
max        3.120271
dtype: float64

По умолчанию медиана всегда включается.

Для нечислового объекта Series, describe() даст простое описание количества уникальных значений и наиболее часто встречающихся значений:

In [100]: s = pd.Series(['a', 'a', 'b', 'b', 'a', 'a', np.nan, 'c', 'd', 'a'])

In [101]: s.describe()
Out[101]: 
count     9
unique    4
top       a
freq      5
dtype: object

Обратите внимание, что для смешанного DataFrame, describe() будет ограничивать вывод только числовыми столбцами, или, если таковых нет, только категориальными:

In [102]: frame = pd.DataFrame({'a': ['Yes', 'Yes', 'No', 'No'], 'b': range(4)})

In [103]: frame.describe()
Out[103]: 
              b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

Это поведение можно контролировать, указав список типов как include/exclude аргументы. Специальное значение all также может быть использовано:

In [104]: frame.describe(include=['object'])
Out[104]: 
         a
count    4
unique   2
top     No
freq     2

In [105]: frame.describe(include=['number'])
Out[105]: 
              b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

In [106]: frame.describe(include='all')
Out[106]: 
          a         b
count     4  4.000000
unique    2       NaN
top      No       NaN
freq      2       NaN
mean    NaN  1.500000
std     NaN  1.290994
min     NaN  0.000000
25%     NaN  0.750000
50%     NaN  1.500000
75%     NaN  2.250000
max     NaN  3.000000

Эта функция полагается на select_dtypes. Обратитесь к ней для получения подробной информации о допустимых входных данных.

Индекс минимальных/максимальных значений

Функции idxmin() и idxmax() для Series и DataFrame вычисляют метки индексов с минимальными и максимальными соответствующими значениями:

In [107]: s1 = pd.Series(np.random.randn(5))

In [108]: s1
Out[108]: 
0   -0.872725
1    1.522411
2    0.080594
3   -1.676067
4    0.435804
dtype: float64

In [109]: s1.idxmin(), s1.idxmax()
Out[109]: (3, 1)

In [110]: df1 = pd.DataFrame(np.random.randn(5,3), columns=['A','B','C'])

In [111]: df1
Out[111]: 
          A         B         C
0  0.445734 -1.649461  0.169660
1  1.246181  0.131682 -2.001988
2 -1.273023  0.870502  0.214583
3  0.088452 -0.173364  1.207466
4  0.546121  0.409515 -0.310515

In [112]: df1.idxmin(axis=0)
Out[112]: 
A    2
B    0
C    1
dtype: int64

In [113]: df1.idxmax(axis=1)
Out[113]: 
0    A
1    A
2    B
3    C
4    A
dtype: object

Если существует несколько строк (или столбцов), соответствующих минимальному или максимальному значению, idxmin() и idxmax() возвращают первый соответствующий индекс:

In [114]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=['A'], index=list('edcba'))

In [115]: df3
Out[115]: 
     A
e  2.0
d  1.0
c  1.0
b  3.0
a  NaN

In [116]: df3['A'].idxmin()
Out[116]: 'd'

Примечание

idxmin и idxmax называются argmin и argmax в NumPy.

Счет значений (гистограммы) / Мода

Метод Series value_counts() и функция верхнего уровня вычисляют гистограмму одномерного массива значений. Ее также можно использовать как функцию для обычных массивов:

In [117]: data = np.random.randint(0, 7, size=50)

In [118]: data
Out[118]: 
array([5, 3, 2, 2, 1, 4, 0, 4, 0, 2, 0, 6, 4, 1, 6, 3, 3, 0, 2, 1, 0, 5, 5,
       3, 6, 1, 5, 6, 2, 0, 0, 6, 3, 3, 5, 0, 4, 3, 3, 3, 0, 6, 1, 3, 5, 5,
       0, 4, 0, 6])

In [119]: s = pd.Series(data)

In [120]: s.value_counts()
Out[120]: 
0    11
3    10
6     7
5     7
4     5
2     5
1     5
dtype: int64

In [121]: pd.value_counts(data)
Out[121]: 
0    11
3    10
6     7
5     7
4     5
2     5
1     5
dtype: int64

Аналогично, вы можете получить наиболее часто встречающиеся значение(я) (моду) значений в Series или DataFrame:

In [122]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])

In [123]: s5.mode()
Out[123]: 
0    3
1    7
dtype: int64

In [124]: df5 = pd.DataFrame({"A": np.random.randint(0, 7, size=50),
   .....:                     "B": np.random.randint(-10, 15, size=50)})
   .....: 

In [125]: df5.mode()
Out[125]: 
   A  B
0  1 -5

Дискретизация и квантилирование

Непрерывные значения можно дискретизировать с помощью функций cut() (бины на основе значений) и qcut() (бины на основе выборочных квантилей):

In [126]: arr = np.random.randn(20)

In [127]: factor = pd.cut(arr, 4)

In [128]: factor
Out[128]: 
[(-0.645, 0.336], (-2.61, -1.626], (-1.626, -0.645], (-1.626, -0.645], (-1.626, -0.645], ..., (0.336, 1.316], (0.336, 1.316], (0.336, 1.316], (0.336, 1.316], (-2.61, -1.626]]
Length: 20
Categories (4, object): [(-2.61, -1.626] < (-1.626, -0.645] < (-0.645, 0.336] < (0.336, 1.316]]

In [129]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])

In [130]: factor
Out[130]: 
[(-1, 0], (-5, -1], (-1, 0], (-5, -1], (-1, 0], ..., (0, 1], (1, 5], (0, 1], (0, 1], (-5, -1]]
Length: 20
Categories (4, object): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]]

qcut() вычисляет выборочные квантили. Например, мы могли бы разбить некоторые данные, распределенные нормально, на равные четверти (квартили):

In [131]: arr = np.random.randn(30)

In [132]: factor = pd.qcut(arr, [0, .25, .5, .75, 1])

In [133]: factor
Out[133]: 
[(-0.139, 1.00736], (1.00736, 1.976], (1.00736, 1.976], [-1.0705, -0.439], [-1.0705, -0.439], ..., (1.00736, 1.976], [-1.0705, -0.439], (-0.439, -0.139], (-0.439, -0.139], (-0.439, -0.139]]
Length: 30
Categories (4, object): [[-1.0705, -0.439] < (-0.439, -0.139] < (-0.139, 1.00736] < (1.00736, 1.976]]

In [134]: pd.value_counts(factor)
Out[134]: 
(1.00736, 1.976]     8
[-1.0705, -0.439]    8
(-0.139, 1.00736]    7
(-0.439, -0.139]     7
dtype: int64

Мы также можем передавать бесконечные значения для определения бинов:

In [135]: arr = np.random.randn(20)

In [136]: factor = pd.cut(arr, [-np.inf, 0, np.inf])

In [137]: factor
Out[137]: 
[(-inf, 0], (0, inf], (0, inf], (0, inf], (-inf, 0], ..., (-inf, 0], (0, inf], (-inf, 0], (-inf, 0], (0, inf]]
Length: 20
Categories (2, object): [(-inf, 0] < (0, inf]]

Применение функций

Чтобы применить свои или функции из других библиотек к объектам pandas, вы должны знать три метода ниже. Подходящий метод зависит от того, ожидает ли ваша функция работать с целым DataFrame или Series, по строкам или столбцам, или поэлементно.

  1. Применение функции к таблице: pipe()
  2. Применение функции к строкам или столбцам: apply()
  3. Элементное применение функции: applymap()

Применение функции к таблице

Новое в версии 0.16.2.

DataFrames и Series конечно же, можно просто передать в функции. Однако, если функция должна вызываться в цепочке, рассмотрите использование метода pipe(). Сравните следующее

# f, g, and h are functions taking and returning ``DataFrames``
>>> f(g(h(df), arg1=1), arg2=2, arg3=3)

с эквивалентным

>>> (df.pipe(h)
       .pipe(g, arg1=1)
       .pipe(f, arg2=2, arg3=3)
    )

Pandas рекомендует второй стиль, который известен как цепочка методов. pipe упрощает использование собственных функций или функций из других библиотек в цепочках методов вместе с методами pandas.

В примере выше, функции f, g, и h каждая ожидала DataFrame в качестве первого позиционного аргумента. А что, если функция, которую вы хотите применить, принимает свои данные, скажем, как второй аргумент? В этом случае, передайте pipe кортеж (callable, data_keyword). .pipe передаст DataFrame в указанный аргумент в кортеже.

Например, мы можем подогнать регрессию, используя statsmodels. Их API ожидает формулу первой и DataFrame как второй аргумент, data. Мы передаём функцию, пару ключ-значение (sm.poisson, 'data') в pipe:

In [138]: import statsmodels.formula.api as sm

In [139]: bb = pd.read_csv('data/baseball.csv', index_col='id')

In [140]: (bb.query('h > 0')
   .....:    .assign(ln_h = lambda df: np.log(df.h))
   .....:    .pipe((sm.poisson, 'data'), 'hr ~ ln_h + year + g + C(lg)')
   .....:    .fit()
   .....:    .summary()
   .....: )
   .....: 
Optimization terminated successfully.
         Current function value: 2.116284
         Iterations 24
Out[140]: 
<class 'statsmodels.iolib.summary.Summary'>
"""
                          Poisson Regression Results                          
==============================================================================
Dep. Variable:                     hr   No. Observations:                   68
Model:                        Poisson   Df Residuals:                       63
Method:                           MLE   Df Model:                            4
Date:                Sat, 24 Dec 2016   Pseudo R-squ.:                  0.6878
Time:                        18:31:33   Log-Likelihood:                -143.91
converged:                       True   LL-Null:                       -460.91
                                        LLR p-value:                6.774e-136
===============================================================================
                  coef    std err          z      P>|z|      [95.0% Conf. Int.]
-------------------------------------------------------------------------------
Intercept   -1267.3636    457.867     -2.768      0.006     -2164.767  -369.960
C(lg)[T.NL]    -0.2057      0.101     -2.044      0.041        -0.403    -0.008
ln_h            0.9280      0.191      4.866      0.000         0.554     1.302
year            0.6301      0.228      2.762      0.006         0.183     1.077
g               0.0099      0.004      2.754      0.006         0.003     0.017
===============================================================================
"""

Метод pipe вдохновлен unix-трубами и более поздними dplyr и magrittr, которые представили популярный (%>%) (читай труба) оператор для R. Реализация pipe здесь достаточно чистая и органично вписывается в python. Мы рекомендуем вам просмотреть исходный код (pd.DataFrame.pipe?? в IPython).

Применение функции к строкам или столбцам

Произвольные функции могут быть применены вдоль осей DataFrame или Panel с помощью метода apply(), который, как и методы описательной статистики, принимает необязательный axis аргумент:

In [141]: df.apply(np.mean)
Out[141]: 
one     -0.251274
three    0.469799
two     -0.191421
dtype: float64

In [142]: df.apply(np.mean, axis=1)
Out[142]: 
a   -0.489066
b    0.273355
c    0.008348
d    0.011457
dtype: float64

In [143]: df.apply(lambda x: x.max() - x.min())
Out[143]: 
one      0.638161
three    1.301762
two      2.237808
dtype: float64

In [144]: df.apply(np.cumsum)
Out[144]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.765438 -0.177289  0.784662
c -0.753821  0.284925  0.335874
d       NaN  1.409398 -0.765684

In [145]: df.apply(np.exp)
Out[145]: 
        one     three       two
a  0.534436       NaN  0.703570
b  0.870320  0.837537  3.115063
c  1.011685  1.587586  0.638401
d       NaN  3.078592  0.332353

В зависимости от возвращаемого типа функции, переданной в apply(), результат будет либо меньшей размерности, либо той же размерности.

apply() в сочетании с некоторыми хитростями может использоваться для ответа на многие вопросы о наборе данных. Например, предположим, что мы хотим извлечь дату, когда максимальное значение для каждого столбца произошло:

In [146]: tsdf = pd.DataFrame(np.random.randn(1000, 3), columns=['A', 'B', 'C'],
   .....:                     index=pd.date_range('1/1/2000', periods=1000))
   .....: 

In [147]: tsdf.apply(lambda x: x.idxmax())
Out[147]: 
A   2001-04-27
B   2002-06-02
C   2000-04-02
dtype: datetime64[ns]

Вы также можете передать дополнительные аргументы и ключевые аргументы в метод apply(). Например, рассмотрим следующую функцию, которую вы хотите применить:

def subtract_and_divide(x, sub, divide=1):
    return (x - sub) / divide

Затем вы можете применить эту функцию следующим образом:

df.apply(subtract_and_divide, args=(5,), divide=3)

Еще одна полезная функция — возможность передавать методы Series для выполнения некоторых операций Series для каждого столбца или строки:

In [148]: tsdf
Out[148]: 
                   A         B         C
2000-01-01  1.796883 -0.930690  3.542846
2000-01-02 -1.242888 -0.695279 -1.000884
2000-01-03 -0.720299  0.546303 -0.082042
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08 -0.527402  0.933507  0.129646
2000-01-09 -0.338903 -1.265452 -1.969004
2000-01-10  0.532566  0.341548  0.150493

In [149]: tsdf.apply(pd.Series.interpolate)
Out[149]: 
                   A         B         C
2000-01-01  1.796883 -0.930690  3.542846
2000-01-02 -1.242888 -0.695279 -1.000884
2000-01-03 -0.720299  0.546303 -0.082042
2000-01-04 -0.681720  0.623743 -0.039704
2000-01-05 -0.643140  0.701184  0.002633
2000-01-06 -0.604561  0.778625  0.044971
2000-01-07 -0.565982  0.856066  0.087309
2000-01-08 -0.527402  0.933507  0.129646
2000-01-09 -0.338903 -1.265452 -1.969004
2000-01-10  0.532566  0.341548  0.150493

Наконец, apply() принимает аргумент raw, который по умолчанию равен False, что преобразует каждую строку или столбец в Series перед применением функции. При значении True переданная функция вместо этого получит объект ndarray, что положительно сказывается на производительности, если вам не нужна функциональность индексирования.

См. также

Раздел GroupBy демонстрирует связанные, гибкие функции для группировки по определенному критерию, применения и объединения результатов в Series, DataFrame и т. д.

Применение элементных Python-функций

Поскольку не все функции могут быть векторизованы (принимать массивы NumPy и возвращать другой массив или значение), методы applymap() в DataFrame и аналогично map() в Series принимают любую Python-функцию, принимающую одно значение и возвращающую одно значение. Например:

In [150]: df4
Out[150]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789
d       NaN  1.124472 -1.101558

In [151]: f = lambda x: len(str(x))

In [152]: df4['one'].map(f)
Out[152]: 
a    14
b    15
c    15
d     3
Name: one, dtype: int64

In [153]: df4.applymap(f)
Out[153]: 
   one  three  two
a   14      3   15
b   15     15   11
c   15     14   15
d    3     13   14

Series.map() обладает дополнительной возможностью, позволяющей легко «связывать» или «отображать» значения, определённые дополнительной серией. Это тесно связано с функциональностью слияния/соединения:

In [154]: s = pd.Series(['six', 'seven', 'six', 'seven', 'six'],
   .....:               index=['a', 'b', 'c', 'd', 'e'])
   .....: 

In [155]: t = pd.Series({'six' : 6., 'seven' : 7.})

In [156]: s
Out[156]: 
a      six
b    seven
c      six
d    seven
e      six
dtype: object

In [157]: s.map(t)
Out[157]: 
a    6.0
b    7.0
c    6.0
d    7.0
e    6.0
dtype: float64

Применение с Panel

Применение с Panel передаст Series в применённую функцию. Если применённая функция возвращает Series, результат применения будет Panel. Если применённая функция сводится к скаляру, результат применения будет DataFrame.

Примечание

До версии 0.13.1 apply на Panel работало только с ufuncs (например, np.sum/np.max).

In [158]: import pandas.util.testing as tm

In [159]: panel = tm.makePanel(5)

In [160]: panel
Out[160]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: ItemA to ItemC
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: A to D

In [161]: panel['ItemA']
Out[161]: 
                   A         B         C         D
2000-01-03  0.330418  1.893177  0.801111  0.528154
2000-01-04  1.761200  0.170247  0.445614 -0.029371
2000-01-05  0.567133 -0.916844  1.453046 -0.631117
2000-01-06 -0.251020  0.835024  2.430373 -0.172441
2000-01-07  1.020099  1.259919  0.653093 -1.020485

Трансформационное применение.

In [162]: result = panel.apply(lambda x: x*2, axis='items')

In [163]: result
Out[163]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: ItemA to ItemC
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: A to D

In [164]: result['ItemA']
Out[164]: 
                   A         B         C         D
2000-01-03  0.660836  3.786354  1.602222  1.056308
2000-01-04  3.522400  0.340494  0.891228 -0.058742
2000-01-05  1.134266 -1.833689  2.906092 -1.262234
2000-01-06 -0.502039  1.670047  4.860747 -0.344882
2000-01-07  2.040199  2.519838  1.306185 -2.040969

Операция сокращения.

In [165]: panel.apply(lambda x: x.dtype, axis='items')
Out[165]: 
                  A        B        C        D
2000-01-03  float64  float64  float64  float64
2000-01-04  float64  float64  float64  float64
2000-01-05  float64  float64  float64  float64
2000-01-06  float64  float64  float64  float64
2000-01-07  float64  float64  float64  float64

Аналогичная операция сокращения.

In [166]: panel.apply(lambda x: x.sum(), axis='major_axis')
Out[166]: 
      ItemA     ItemB     ItemC
A  3.427831 -2.581431  0.840809
B  3.241522 -1.409935 -1.114512
C  5.783237  0.319672 -0.431906
D -1.325260 -2.914834  0.857043

Это последнее сокращение эквивалентно

In [167]: panel.sum('major_axis')
Out[167]: 
      ItemA     ItemB     ItemC
A  3.427831 -2.581431  0.840809
B  3.241522 -1.409935 -1.114512
C  5.783237  0.319672 -0.431906
D -1.325260 -2.914834  0.857043

Трансформационная операция, возвращающая Panel, но вычисляющая z-оценку по major_axis.

In [168]: result = panel.apply(
   .....:            lambda x: (x-x.mean())/x.std(),
   .....:            axis='major_axis')
   .....: 

In [169]: result
Out[169]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: ItemA to ItemC
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: A to D

In [170]: result['ItemA']
Out[170]: 
                   A         B         C         D
2000-01-03 -0.469761  1.156225 -0.441347  1.341731
2000-01-04  1.422763 -0.444015 -0.882647  0.398661
2000-01-05 -0.156654 -1.453694  0.367936 -0.619210
2000-01-06 -1.238841  0.173423  1.581149  0.156654
2000-01-07  0.442494  0.568061 -0.625091 -1.277837

Apply также может принимать несколько осей в аргументе axis. Это передаст DataFrame поперечного сечения в применённую функцию.

In [171]: f = lambda x: ((x.T-x.mean(1))/x.std(1)).T

In [172]: result = panel.apply(f, axis = ['items','major_axis'])

In [173]: result
Out[173]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 4 (items) x 5 (major_axis) x 3 (minor_axis)
Items axis: A to D
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: ItemA to ItemC

In [174]: result.loc[:,:,'ItemA']
Out[174]: 
                   A         B         C         D
2000-01-03  0.864236  1.132969  0.557316  0.575106
2000-01-04  0.795745  0.652527  0.534808 -0.070674
2000-01-05 -0.310864  0.558627  1.086688 -1.051477
2000-01-06 -0.001065  0.832460  0.846006  0.043602
2000-01-07  1.128946  1.152469 -0.218186 -0.891680

Это эквивалентно следующему

In [175]: result = pd.Panel(dict([ (ax, f(panel.loc[:,:,ax]))
   .....:                         for ax in panel.minor_axis ]))
   .....: 

In [176]: result
Out[176]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 4 (items) x 5 (major_axis) x 3 (minor_axis)
Items axis: A to D
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: ItemA to ItemC

In [177]: result.loc[:,:,'ItemA']
Out[177]: 
                   A         B         C         D
2000-01-03  0.864236  1.132969  0.557316  0.575106
2000-01-04  0.795745  0.652527  0.534808 -0.070674
2000-01-05 -0.310864  0.558627  1.086688 -1.051477
2000-01-06 -0.001065  0.832460  0.846006  0.043602
2000-01-07  1.128946  1.152469 -0.218186 -0.891680

Переиндексация и изменение меток

reindex() — это фундаментальный метод выравнивания данных в pandas. Он используется для реализации почти всех других функций, использующих функциональность выравнивания по меткам. Переиндексация означает приведение данных к соответствию заданному набору меток по определённой оси. Это выполняет несколько вещей:

  • Переупорядочивает существующие данные в соответствии с новым набором меток
  • Вставляет маркеры пропущенных значений (NA) в позиции меток, где не было данных для этой метки
  • При необходимости, заполняет данные для пропущенных меток с помощью логики (очень важно при работе с временными рядами)

Вот простой пример:

In [178]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

In [179]: s
Out[179]: 
a   -1.010924
b   -0.672504
c   -1.139222
d    0.354653
e    0.563622
dtype: float64

In [180]: s.reindex(['e', 'b', 'f', 'd'])
Out[180]: 
e    0.563622
b   -0.672504
f         NaN
d    0.354653
dtype: float64

Здесь метка f не содержалась в Series и поэтому появляется как NaN в результате.

В DataFrame вы можете одновременно переиндексировать индекс и столбцы:

In [181]: df
Out[181]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789
d       NaN  1.124472 -1.101558

In [182]: df.reindex(index=['c', 'f', 'b'], columns=['three', 'two', 'one'])
Out[182]: 
      three       two       one
c  0.462215 -0.448789  0.011617
f       NaN       NaN       NaN
b -0.177289  1.136249 -0.138894

Для удобства вы можете использовать метод reindex_axis(), который принимает метки и ключевой axis параметр.

Обратите внимание, что объекты Index содержащие фактические метки осей, могут быть **общими** между объектами. Итак, если у нас есть Series и DataFrame, можно сделать следующее:

In [183]: rs = s.reindex(df.index)

In [184]: rs
Out[184]: 
a   -1.010924
b   -0.672504
c   -1.139222
d    0.354653
dtype: float64

In [185]: rs.index is df.index
Out[185]: True

Это означает, что индекс переиндексированного Series — это тот же объект Python, что и индекс DataFrame.

См. также

MultiIndex/Расширенная индексация — ещё более компактный способ переиндексации.

Примечание

При написании производительного кода есть веская причина уделить время изучению переиндексации: многие операции быстрее на предварительно выровненных данных. Добавление двух невыровненных DataFrame внутри приводит к шагу переиндексации. При исследовательском анализе вы едва заметите разницу (потому что reindex был сильно оптимизирован), но когда счёт идёт на секунды, добавление нескольких явных вызовов reindex здесь и там может иметь влияние.

Переиндексация для выравнивания с другим объектом

Вы можете захотеть взять объект и переиндексировать его оси так, чтобы они были помечены одинаково как у другого объекта. Хотя синтаксис для этого прост, но громоздкий, эта операция достаточно распространена, и метод reindex_like() доступен для упрощения этой задачи:

In [186]: df2
Out[186]: 
        one       two
a -0.626544 -0.351587
b -0.138894  1.136249
c  0.011617 -0.448789

In [187]: df3
Out[187]: 
        one       two
a -0.375270 -0.463545
b  0.112379  1.024292
c  0.262891 -0.560746

In [188]: df.reindex_like(df2)
Out[188]: 
        one       two
a -0.626544 -0.351587
b -0.138894  1.136249
c  0.011617 -0.448789

Выравнивание объектов друг с другом с помощью align

Метод align() — самый быстрый способ одновременного выравнивания двух объектов. Он поддерживает аргумент join (связанный с соединением и слиянием):

  • join='outer': взять объединение индексов (по умолчанию)
  • join='left': использовать индекс вызываемого объекта
  • join='right': использовать индекс переданного объекта
  • join='inner': пересечь индексы

Возвращает кортеж с обеими переиндексированными Series:

In [189]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

In [190]: s1 = s[:4]

In [191]: s2 = s[1:]

In [192]: s1.align(s2)
Out[192]: 
(a   -0.365106
 b    1.092702
 c   -1.481449
 d    1.781190
 e         NaN
 dtype: float64, a         NaN
 b    1.092702
 c   -1.481449
 d    1.781190
 e   -0.031543
 dtype: float64)

In [193]: s1.align(s2, join='inner')
Out[193]: 
(b    1.092702
 c   -1.481449
 d    1.781190
 dtype: float64, b    1.092702
 c   -1.481449
 d    1.781190
 dtype: float64)

In [194]: s1.align(s2, join='left')
Out[194]: 
(a   -0.365106
 b    1.092702
 c   -1.481449
 d    1.781190
 dtype: float64, a         NaN
 b    1.092702
 c   -1.481449
 d    1.781190
 dtype: float64)

Для DataFrames, метод join по умолчанию применяется как к индексу, так и к столбцам:

In [195]: df.align(df2, join='inner')
Out[195]: 
(        one       two
 a -0.626544 -0.351587
 b -0.138894  1.136249
 c  0.011617 -0.448789,         one       two
 a -0.626544 -0.351587
 b -0.138894  1.136249
 c  0.011617 -0.448789)

Вы также можете передать axis опцию, чтобы выровнять только по указанной оси:

In [196]: df.align(df2, join='inner', axis=0)
Out[196]: 
(        one     three       two
 a -0.626544       NaN -0.351587
 b -0.138894 -0.177289  1.136249
 c  0.011617  0.462215 -0.448789,         one       two
 a -0.626544 -0.351587
 b -0.138894  1.136249
 c  0.011617 -0.448789)

Если вы передадите Series в DataFrame.align(), вы можете выбрать выравнивание обоих объектов по индексу DataFrame или столбцам, используя параметр axis:

In [197]: df.align(df2.ix[0], axis=1)
Out[197]: 
(        one     three       two
 a -0.626544       NaN -0.351587
 b -0.138894 -0.177289  1.136249
 c  0.011617  0.462215 -0.448789
 d       NaN  1.124472 -1.101558, one     -0.626544
 three         NaN
 two     -0.351587
 Name: a, dtype: float64)

Заполнение при переиндексации

reindex() принимает необязательный параметр method, который представляет собой метод заполнения, выбранный из следующей таблицы:

Метод Действие
pad / ffill Заполнить значения вперёд
bfill / backfill Заполнить значения назад
nearest Заполнить из ближайшего значения индекса

Мы проиллюстрируем эти методы заполнения на простом Series:

In [198]: rng = pd.date_range('1/3/2000', periods=8)

In [199]: ts = pd.Series(np.random.randn(8), index=rng)

In [200]: ts2 = ts[[0, 3, 6]]

In [201]: ts
Out[201]: 
2000-01-03    0.480993
2000-01-04    0.604244
2000-01-05   -0.487265
2000-01-06    1.990533
2000-01-07    0.327007
2000-01-08    1.053639
2000-01-09   -2.927808
2000-01-10    0.082065
Freq: D, dtype: float64

In [202]: ts2
Out[202]: 
2000-01-03    0.480993
2000-01-06    1.990533
2000-01-09   -2.927808
dtype: float64

In [203]: ts2.reindex(ts.index)
Out[203]: 
2000-01-03    0.480993
2000-01-04         NaN
2000-01-05         NaN
2000-01-06    1.990533
2000-01-07         NaN
2000-01-08         NaN
2000-01-09   -2.927808
2000-01-10         NaN
Freq: D, dtype: float64

In [204]: ts2.reindex(ts.index, method='ffill')
Out[204]: 
2000-01-03    0.480993
2000-01-04    0.480993
2000-01-05    0.480993
2000-01-06    1.990533
2000-01-07    1.990533
2000-01-08    1.990533
2000-01-09   -2.927808
2000-01-10   -2.927808
Freq: D, dtype: float64

In [205]: ts2.reindex(ts.index, method='bfill')
Out[205]: 
2000-01-03    0.480993
2000-01-04    1.990533
2000-01-05    1.990533
2000-01-06    1.990533
2000-01-07   -2.927808
2000-01-08   -2.927808
2000-01-09   -2.927808
2000-01-10         NaN
Freq: D, dtype: float64

In [206]: ts2.reindex(ts.index, method='nearest')
Out[206]: 
2000-01-03    0.480993
2000-01-04    0.480993
2000-01-05    1.990533
2000-01-06    1.990533
2000-01-07    1.990533
2000-01-08   -2.927808
2000-01-09   -2.927808
2000-01-10   -2.927808
Freq: D, dtype: float64

Эти методы требуют, чтобы индексы были упорядочены, в порядке возрастания или убывания.

Обратите внимание, что тот же результат можно было бы получить, используя fillna (кроме method='nearest') или interpolate:

In [207]: ts2.reindex(ts.index).fillna(method='ffill')
Out[207]: 
2000-01-03    0.480993
2000-01-04    0.480993
2000-01-05    0.480993
2000-01-06    1.990533
2000-01-07    1.990533
2000-01-08    1.990533
2000-01-09   -2.927808
2000-01-10   -2.927808
Freq: D, dtype: float64

reindex() сгенерирует ValueError, если индекс не является монотонно возрастающим или убывающим. fillna() и interpolate() не проверяют порядок индекса.

Ограничения на заполнение при переиндексации

Аргументы limit и tolerance обеспечивают дополнительный контроль над заполнением при переиндексации. Limit определяет максимальное количество последовательных совпадений:

In [208]: ts2.reindex(ts.index, method='ffill', limit=1)
Out[208]: 
2000-01-03    0.480993
2000-01-04    0.480993
2000-01-05         NaN
2000-01-06    1.990533
2000-01-07    1.990533
2000-01-08         NaN
2000-01-09   -2.927808
2000-01-10   -2.927808
Freq: D, dtype: float64

В отличие от этого, tolerance определяет максимальное расстояние между значениями индекса и индексатора:

In [209]: ts2.reindex(ts.index, method='ffill', tolerance='1 day')
Out[209]: 
2000-01-03    0.480993
2000-01-04    0.480993
2000-01-05         NaN
2000-01-06    1.990533
2000-01-07    1.990533
2000-01-08         NaN
2000-01-09   -2.927808
2000-01-10   -2.927808
Freq: D, dtype: float64

Обратите внимание, что при использовании с DatetimeIndex, TimedeltaIndex или PeriodIndex, tolerance будет приведено к Timedelta, если это возможно. Это позволяет указать tolerance с соответствующими строками.

Удаление меток с оси

Метод, тесно связанный с reindex это функция drop(). Она удаляет набор меток с оси:

In [210]: df
Out[210]: 
        one     three       two
a -0.626544       NaN -0.351587
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789
d       NaN  1.124472 -1.101558

In [211]: df.drop(['a', 'd'], axis=0)
Out[211]: 
        one     three       two
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789

In [212]: df.drop(['one'], axis=1)
Out[212]: 
      three       two
a       NaN -0.351587
b -0.177289  1.136249
c  0.462215 -0.448789
d  1.124472 -1.101558

Обратите внимание, что следующее также работает, но немного менее очевидно / чисто:

In [213]: df.reindex(df.index.difference(['a', 'd']))
Out[213]: 
        one     three       two
b -0.138894 -0.177289  1.136249
c  0.011617  0.462215 -0.448789

Переименование/сопоставление меток

Метод rename() позволяет переименовать ось на основе некоторого отображения (словарь или Series) или произвольной функции.

In [214]: s
Out[214]: 
a   -0.365106
b    1.092702
c   -1.481449
d    1.781190
e   -0.031543
dtype: float64

In [215]: s.rename(str.upper)
Out[215]: 
A   -0.365106
B    1.092702
C   -1.481449
D    1.781190
E   -0.031543
dtype: float64

Если вы передаёте функцию, она должна возвращать значение при вызове с любой из меток (и должна создавать набор уникальных значений). Также можно использовать словарь или Series:

In [216]: df.rename(columns={'one' : 'foo', 'two' : 'bar'},
   .....:           index={'a' : 'apple', 'b' : 'banana', 'd' : 'durian'})
   .....: 
Out[216]: 
             foo     three       bar
apple  -0.626544       NaN -0.351587
banana -0.138894 -0.177289  1.136249
c       0.011617  0.462215 -0.448789
durian       NaN  1.124472 -1.101558

Если отображение не включает метку столбца/индекса, она не переименовывается. Дополнительные метки в отображении также не вызывают ошибку.

Метод rename() также предоставляет параметр inplace, который по умолчанию False и копирует исходные данные. Передайте inplace=True для переименования данных на месте.

Новое в версии 0.18.0.

Наконец, rename() также принимает скаляр или список для изменения атрибута Series.name.

In [217]: s.rename("scalar-name")
Out[217]: 
a   -0.365106
b    1.092702
c   -1.481449
d    1.781190
e   -0.031543
Name: scalar-name, dtype: float64

Класс Panel имеет связанный класс rename_axis(), который может переименовать любую из его трёх осей.

Итерация

Поведение базовой итерации по объектам pandas зависит от типа. При итерации по Series, она рассматривается как массивоподобный объект, и базовая итерация возвращает значения. Другие структуры данных, такие как DataFrame и Panel, следуют соглашению итерации по типу «словарь», перебирая «ключи» объектов.

Короче говоря, базовая итерация (for i in object) возвращает:

  • Series: значения
  • DataFrame: метки столбцов
  • Panel: метки элементов

Таким образом, например, итерация по DataFrame даёт вам имена столбцов:

In [218]: df = pd.DataFrame({'col1' : np.random.randn(3), 'col2' : np.random.randn(3)},
   .....:                   index=['a', 'b', 'c'])
   .....: 

In [219]: for col in df:
   .....:     print(col)
   .....: 
col1
col2

Объекты Pandas также имеют метод iteritems() (подобно словарю), для итерации по парам «ключ-значение».

Чтобы пройтись по строкам DataFrame, можно использовать следующие методы:

  • iterrows(): Итерирование по строкам DataFrame как пары (индекс, Series). Это преобразует строки в объекты Series, что может изменить типы данных и имеет некоторые последствия для производительности.
  • itertuples(): Итерирование по строкам DataFrame как namedtuple значений. Это намного быстрее, чем iterrows(), и во многих случаях предпочтительнее для итерации по значениям DataFrame.

Предупреждение

Итерация по объектам pandas, как правило, медленная. Во многих случаях ручная итерация по строкам не нужна и может быть избегнута с помощью одного из следующих подходов:

  • Поиск векторизованного решения: многие операции могут быть выполнены с помощью встроенных методов или функций numpy, (булевых) индексов и т. д.
  • Если у вас есть функция, которая не может работать с полным DataFrame/Series за один раз, лучше использовать apply() вместо итерации по значениям. См. документацию по применению функций.
  • Если вам нужно выполнять итерационные манипуляции со значениями, но производительность важна, рассмотрите возможность написания внутреннего цикла с использованием, например, cython или numba. См. раздел улучшение производительности для некоторых примеров такого подхода.

Предупреждение

Вы никогда не должны изменять то, по чему вы итерируетесь. Это не гарантируется, что будет работать во всех случаях. В зависимости от типов данных итератор возвращает копию, а не представление, и запись в неё не повлияет!

Например, в следующем случае установка значения не имеет никакого эффекта:

In [220]: df = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})

In [221]: for index, row in df.iterrows():
   .....:     row['a'] = 10
   .....: 

In [222]: df
Out[222]: 
   a  b
0  1  a
1  2  b
2  3  c

iteritems

В соответствии с интерфейсом типа «словарь», iteritems() итерируется по парам «ключ-значение»:

  • Series: пары (индекс, скалярное значение)
  • DataFrame: пары (столбец, Series)
  • Panel: пары (элемент, DataFrame)

Например:

In [223]: for item, frame in wp.iteritems():
   .....:     print(item)
   .....:     print(frame)
   .....: 
Item1
                   A         B         C         D
2000-01-01 -1.032011  0.969818 -0.962723  1.382083
2000-01-02 -0.938794  0.669142 -0.433567 -0.273610
2000-01-03  0.680433 -0.308450 -0.276099 -1.821168
2000-01-04 -1.993606 -1.927385 -2.027924  1.624972
2000-01-05  0.551135  3.059267  0.455264 -0.030740
Item2
                   A         B         C         D
2000-01-01  0.935716  1.061192 -2.107852  0.199905
2000-01-02  0.323586 -0.641630 -0.587514  0.053897
2000-01-03  0.194889 -0.381994  0.318587  2.089075
2000-01-04 -0.728293 -0.090255 -0.748199  1.318931
2000-01-05 -2.029766  0.792652  0.461007 -0.542749

iterrows

iterrows() позволяет проходить по строкам DataFrame как объектам Series. Он возвращает итератор, возвращающий каждое значение индекса вместе со Series, содержащей данные в каждой строке:

In [224]: for row_index, row in df.iterrows():
   .....:     print('%s\n%s' % (row_index, row))
   .....: 
0
a    1
b    a
Name: 0, dtype: object
1
a    2
b    b
Name: 1, dtype: object
2
a    3
b    c
Name: 2, dtype: object

Примечание

Поскольку iterrows() возвращает Series для каждой строки, он не сохраняет типы данных между строками (типы данных сохраняются между столбцами для DataFrame). Например,

In [225]: df_orig = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])

In [226]: df_orig.dtypes
Out[226]: 
int        int64
float    float64
dtype: object

In [227]: row = next(df_orig.iterrows())[1]

In [228]: row
Out[228]: 
int      1.0
float    1.5
Name: 0, dtype: float64

Все значения в row, возвращаемые как Series, теперь повышаются до float, а также исходное целочисленное значение в столбце x:

In [229]: row['int'].dtype
Out[229]: dtype('float64')

In [230]: df_orig['int'].dtype
Out[230]: dtype('int64')

Для сохранения типов данных при итерации по строкам лучше использовать itertuples(), который возвращает namedtuple значений и который, как правило, намного быстрее, чем iterrows.

Например, выдуманный способ транспонирования DataFrame:

In [231]: df2 = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]})

In [232]: print(df2)
   x  y
0  1  4
1  2  5
2  3  6

In [233]: print(df2.T)
   0  1  2
x  1  2  3
y  4  5  6

In [234]: df2_t = pd.DataFrame(dict((idx,values) for idx, values in df2.iterrows()))

In [235]: print(df2_t)
   0  1  2
x  1  2  3
y  4  5  6

itertuples

Метод itertuples() вернёт итератор, возвращающий namedtuple для каждой строки в DataFrame. Первый элемент кортежа будет соответствующим значением индекса строки, а оставшиеся значения — значениями строки.

Например,

In [236]: for row in df.itertuples():
   .....:     print(row)
   .....: 
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c')

Этот метод не преобразует строку в объект Series, а только возвращает значения внутри namedtuple. Поэтому, itertuples() сохраняет тип данных значений и, как правило, быстрее, чем iterrows().

Примечание

Имена столбцов будут переименованы в позиционные имена, если они являются недопустимыми идентификаторами Python, повторяющимися или начинаются с подчеркивания. При большом количестве столбцов (>255) возвращаются обычные кортежи.

.dt аксессор

Series имеет аксессор для краткого возврата свойств типа datetime для *значений* Series, если это Series типа datetime/period. Это вернет Series, индексированную так же, как и существующая Series.

# datetime
In [237]: s = pd.Series(pd.date_range('20130101 09:10:12', periods=4))

In [238]: s
Out[238]: 
0   2013-01-01 09:10:12
1   2013-01-02 09:10:12
2   2013-01-03 09:10:12
3   2013-01-04 09:10:12
dtype: datetime64[ns]

In [239]: s.dt.hour
Out[239]: 
0    9
1    9
2    9
3    9
dtype: int64

In [240]: s.dt.second
Out[240]: 
0    12
1    12
2    12
3    12
dtype: int64

In [241]: s.dt.day
Out[241]: 
0    1
1    2
2    3
3    4
dtype: int64

Это позволяет создавать выражения, подобные этому:

In [242]: s[s.dt.day==2]
Out[242]: 
1   2013-01-02 09:10:12
dtype: datetime64[ns]

Вы можете легко создавать преобразования, учитывающие часовой пояс:

In [243]: stz = s.dt.tz_localize('US/Eastern')

In [244]: stz
Out[244]: 
0   2013-01-01 09:10:12-05:00
1   2013-01-02 09:10:12-05:00
2   2013-01-03 09:10:12-05:00
3   2013-01-04 09:10:12-05:00
dtype: datetime64[ns, US/Eastern]

In [245]: stz.dt.tz
Out[245]: <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>

Вы также можете объединять эти типы операций:

In [246]: s.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[246]: 
0   2013-01-01 04:10:12-05:00
1   2013-01-02 04:10:12-05:00
2   2013-01-03 04:10:12-05:00
3   2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern]

Вы также можете форматировать значения datetime как строки с помощью Series.dt.strftime(), который поддерживает тот же формат, что и стандартный strftime().

# DatetimeIndex
In [247]: s = pd.Series(pd.date_range('20130101', periods=4))

In [248]: s
Out[248]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
3   2013-01-04
dtype: datetime64[ns]

In [249]: s.dt.strftime('%Y/%m/%d')
Out[249]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object
# PeriodIndex
In [250]: s = pd.Series(pd.period_range('20130101', periods=4))

In [251]: s
Out[251]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
3   2013-01-04
dtype: object

In [252]: s.dt.strftime('%Y/%m/%d')
Out[252]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object

Аксессор .dt работает для типов period и timedelta.

# period
In [253]: s = pd.Series(pd.period_range('20130101', periods=4, freq='D'))

In [254]: s
Out[254]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
3   2013-01-04
dtype: object

In [255]: s.dt.year
Out[255]: 
0    2013
1    2013
2    2013
3    2013
dtype: int64

In [256]: s.dt.day
Out[256]: 
0    1
1    2
2    3
3    4
dtype: int64
# timedelta
In [257]: s = pd.Series(pd.timedelta_range('1 day 00:00:05', periods=4, freq='s'))

In [258]: s
Out[258]: 
0   1 days 00:00:05
1   1 days 00:00:06
2   1 days 00:00:07
3   1 days 00:00:08
dtype: timedelta64[ns]

In [259]: s.dt.days
Out[259]: 
0    1
1    1
2    1
3    1
dtype: int64

In [260]: s.dt.seconds
Out[260]: 
0    5
1    6
2    7
3    8
dtype: int64

In [261]: s.dt.components
Out[261]: 
   days  hours  minutes  seconds  milliseconds  microseconds  nanoseconds
0     1      0        0        5             0             0            0
1     1      0        0        6             0             0            0
2     1      0        0        7             0             0            0
3     1      0        0        8             0             0            0

Примечание

Series.dt вызовет TypeError при доступе к значениям, не являющимся datetimelike.

Векторизованные методы строк

Series оснащены набором методов обработки строк, которые упрощают работу с каждым элементом массива. Возможно, самое важное, что эти методы автоматически исключают отсутствующие/NA значения. К ним можно получить доступ через атрибут str Series, и у них, как правило, есть имена, соответствующие эквивалентным (скалярным) встроенным методам строк. Например:

In [262]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])

In [263]: s.str.lower()
Out[263]: 
0       a
1       b
2       c
3    aaba
4    baca
5     NaN
6    caba
7     dog
8     cat
dtype: object

Также предоставляются мощные методы сопоставления с образцом, но обратите внимание, что сопоставление с образцом по умолчанию использует регулярные выражения (regular expressions) (и в некоторых случаях всегда их использует).

Полное описание см. в разделе Векторизованные методы строк.

Сортировка

Предупреждение

API сортировки существенно изменен в версии 0.17.0, см. здесь для этих изменений. В частности, все методы сортировки теперь по умолчанию возвращают новый объект и **НЕ** работают на месте (за исключением передачи inplace=True).

Существует два очевидных типа сортировки, которые могут вас заинтересовать: сортировка по меткам и сортировка по фактическим значениям.

По индексу

Основными методами сортировки меток осей (индексов) являются Series.sort_index() и DataFrame.sort_index() методы.

In [264]: unsorted_df = df.reindex(index=['a', 'd', 'c', 'b'],
   .....:                          columns=['three', 'two', 'one'])
   .....: 

# DataFrame
In [265]: unsorted_df.sort_index()
Out[265]: 
   three  two  one
a    NaN  NaN  NaN
b    NaN  NaN  NaN
c    NaN  NaN  NaN
d    NaN  NaN  NaN

In [266]: unsorted_df.sort_index(ascending=False)
Out[266]: 
   three  two  one
d    NaN  NaN  NaN
c    NaN  NaN  NaN
b    NaN  NaN  NaN
a    NaN  NaN  NaN

In [267]: unsorted_df.sort_index(axis=1)
Out[267]: 
   one  three  two
a  NaN    NaN  NaN
d  NaN    NaN  NaN
c  NaN    NaN  NaN
b  NaN    NaN  NaN

# Series
In [268]: unsorted_df['three'].sort_index()
Out[268]: 
a   NaN
b   NaN
c   NaN
d   NaN
Name: three, dtype: float64

По значениям

Методы Series.sort_values() и DataFrame.sort_values() являются точками входа для сортировки по значениям (то есть значения в столбце или строке). DataFrame.sort_values() может принимать необязательный аргумент by для axis=0, который будет использовать произвольный вектор или имя столбца DataFrame для определения порядка сортировки:

In [269]: df1 = pd.DataFrame({'one':[2,1,1,1],'two':[1,3,2,4],'three':[5,4,3,2]})

In [270]: df1.sort_values(by='two')
Out[270]: 
   one  three  two
0    2      5    1
2    1      3    2
1    1      4    3
3    1      2    4

Аргумент by может принимать список имен столбцов, например:

In [271]: df1[['one', 'two', 'three']].sort_values(by=['one','two'])
Out[271]: 
   one  two  three
2    1    2      3
1    1    3      4
3    1    4      2
0    2    1      5

Эти методы имеют специальное обращение с NA-значениями с помощью аргумента na_position:

In [272]: s[2] = np.nan

In [273]: s.sort_values()
Out[273]: 
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
2     NaN
5     NaN
dtype: object

In [274]: s.sort_values(na_position='first')
Out[274]: 
2     NaN
5     NaN
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
dtype: object

searchsorted

Series имеет метод searchsorted(), который работает аналогично numpy.ndarray.searchsorted().

In [275]: ser = pd.Series([1, 2, 3])

In [276]: ser.searchsorted([0, 3])
Out[276]: array([0, 2])

In [277]: ser.searchsorted([0, 4])
Out[277]: array([0, 3])

In [278]: ser.searchsorted([1, 3], side='right')
Out[278]: array([1, 3])

In [279]: ser.searchsorted([1, 3], side='left')
Out[279]: array([0, 2])

In [280]: ser = pd.Series([3, 1, 2])

In [281]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[281]: array([0, 2])

наименьшие / наибольшие значения

Введено в версии 0.14.0.

Series имеет методы nsmallest() и nlargest(), которые возвращают наименьшие или наибольшие n значений. Для большого Series это может быть намного быстрее, чем сортировка всего Series и вызов head(n) на результате.

In [282]: s = pd.Series(np.random.permutation(10))

In [283]: s
Out[283]: 
0    9
1    8
2    5
3    3
4    6
5    7
6    0
7    2
8    4
9    1
dtype: int64

In [284]: s.sort_values()
Out[284]: 
6    0
9    1
7    2
3    3
8    4
2    5
4    6
5    7
1    8
0    9
dtype: int64

In [285]: s.nsmallest(3)
Out[285]: 
6    0
9    1
7    2
dtype: int64

In [286]: s.nlargest(3)
Out[286]: 
0    9
1    8
5    7
dtype: int64

Введено в версии 0.17.0.

DataFrame также имеет методы nlargest и nsmallest.

In [287]: df = pd.DataFrame({'a': [-2, -1, 1, 10, 8, 11, -1],
   .....:                    'b': list('abdceff'),
   .....:                    'c': [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0]})
   .....: 

In [288]: df.nlargest(3, 'a')
Out[288]: 
    a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN

In [289]: df.nlargest(5, ['a', 'c'])
Out[289]: 
    a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN
2   1  d  4.0
1  -1  b  2.0
6  -1  f  4.0

In [290]: df.nsmallest(3, 'a')
Out[290]: 
   a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0
1 -1  b  2.0
6 -1  f  4.0

In [291]: df.nsmallest(5, ['a', 'c'])
Out[291]: 
   a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0
1 -1  b  2.0
6 -1  f  4.0
2  1  d  4.0
4  8  e  NaN

Сортировка по столбцу с многоуровневым индексом

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

In [292]: df1.columns = pd.MultiIndex.from_tuples([('a','one'),('a','two'),('b','three')])

In [293]: df1.sort_values(by=('a','two'))
Out[293]: 
    a         b
  one two three
3   1   2     4
2   1   3     2
1   1   4     3
0   2   5     1

Копирование

Метод copy() для объектов pandas копирует исходные данные (хотя не индексы осей, поскольку они неизменяемы) и возвращает новый объект. Обратите внимание, что **копировать объекты редко необходимо**. Например, есть всего несколько способов изменить DataFrame *на месте*:

  • Вставка, удаление или изменение столбца
  • Присвоение атрибутам index или columns
  • Для однородных данных, прямое изменение значений через атрибут values или расширенный индексирование

Важно отметить, что ни один метод pandas не вызывает побочного эффекта изменения ваших данных; почти все методы возвращают новые объекты, оставляя исходный объект нетронутым. Если данные изменяются, это происходит потому, что вы сделали это явно.

Типы данных

Основные типы данных, хранящиеся в объектах pandas, это float, int, bool, datetime64[ns] и datetime64[ns, tz] (в >= 0.17.0), timedelta[ns], category (в >= 0.15.0) и object. Кроме того, эти типы данных имеют размер элементов, например int64 и int32. См. Series с часовым поясом для получения более подробной информации о типах данных datetime64[ns, tz].

Удобный атрибут dtypes для DataFrames возвращает Series с типом данных каждого столбца.

In [294]: dft = pd.DataFrame(dict(A = np.random.rand(3),
   .....:                         B = 1,
   .....:                         C = 'foo',
   .....:                         D = pd.Timestamp('20010102'),
   .....:                         E = pd.Series([1.0]*3).astype('float32'),
   .....:                                     F = False,
   .....:                                     G = pd.Series([1]*3,dtype='int8')))
   .....: 

In [295]: dft
Out[295]: 
          A  B    C          D    E      F  G
0  0.954940  1  foo 2001-01-02  1.0  False  1
1  0.318163  1  foo 2001-01-02  1.0  False  1
2  0.985803  1  foo 2001-01-02  1.0  False  1

In [296]: dft.dtypes
Out[296]: 
A           float64
B             int64
C            object
D    datetime64[ns]
E           float32
F              bool
G              int8
dtype: object

Для Series используйте атрибут dtype.

In [297]: dft['A'].dtype
Out[297]: dtype('float64')

Если объект pandas содержит данные нескольких типов данных *В ОДНОМ СТОЛБЦЕ*, тип данных столбца будет выбран для поддержки всех типов данных (object является наиболее общим).

# these ints are coerced to floats
In [298]: pd.Series([1, 2, 3, 4, 5, 6.])
Out[298]: 
0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
5    6.0
dtype: float64

# string data forces an ``object`` dtype
In [299]: pd.Series([1, 2, 3, 6., 'foo'])
Out[299]: 
0      1
1      2
2      3
3      6
4    foo
dtype: object

Метод get_dtype_counts() вернёт количество столбцов каждого типа в DataFrame:

In [300]: dft.get_dtype_counts()
Out[300]: 
bool              1
datetime64[ns]    1
float32           1
float64           1
int64             1
int8              1
object            1
dtype: int64

Числовые типы данных будут распространяться и могут сосуществовать в DataFrame (начиная с версии 0.11.0). Если тип данных передаётся (прямо через ключевое слово dtype, переданный ndarray, или переданный Series, то он сохранится в операциях DataFrame. Кроме того, различные числовые типы данных **НЕ** будут комбинироваться. Следующий пример позволит вам это почувствовать.

In [301]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=['A'], dtype='float32')

In [302]: df1
Out[302]: 
          A
0  0.647650
1  0.822993
2  1.778703
3 -1.543048
4 -0.123256
5  2.239740
6 -0.143778
7 -2.885090

In [303]: df1.dtypes
Out[303]: 
A    float32
dtype: object

In [304]: df2 = pd.DataFrame(dict( A = pd.Series(np.random.randn(8), dtype='float16'),
   .....:                         B = pd.Series(np.random.randn(8)),
   .....:                         C = pd.Series(np.array(np.random.randn(8), dtype='uint8')) ))
   .....: 

In [305]: df2
Out[305]: 
          A         B    C
0  0.027588  0.296947    0
1 -1.150391  0.007045  255
2  0.246460  0.707877    1
3 -0.455078  0.950661    0
4 -1.507812  0.087527    0
5 -0.502441 -0.339212    0
6  0.528809 -0.278698    0
7  0.590332  1.775379    0

In [306]: df2.dtypes
Out[306]: 
A    float16
B    float64
C      uint8
dtype: object

Значения по умолчанию

По умолчанию целочисленные типы данных являются int64, а плавающие типы данных float64, *НЕЗАВИСИМО* от платформы (32-битная или 64-битная). Следующие все приведут к типам данных int64.

In [307]: pd.DataFrame([1, 2], columns=['a']).dtypes
Out[307]: 
a    int64
dtype: object

In [308]: pd.DataFrame({'a': [1, 2]}).dtypes
Out[308]: 
a    int64
dtype: object

In [309]: pd.DataFrame({'a': 1 }, index=list(range(2))).dtypes
Out[309]: 
a    int64
dtype: object

Однако Numpy выберет *зависимые от платформы* типы при создании массивов. Следующее **ПОДТВЕРДИТ** результат int32 на 32-битной платформе.

In [310]: frame = pd.DataFrame(np.array([1, 2]))

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

Типы могут потенциально быть *преобразованы* при объединении с другими типами, что означает их повышение с текущего типа (скажем int до float).

In [311]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2

In [312]: df3
Out[312]: 
          A         B      C
0  0.675238  0.296947    0.0
1 -0.327398  0.007045  255.0
2  2.025163  0.707877    1.0
3 -1.998126  0.950661    0.0
4 -1.631068  0.087527    0.0
5  1.737299 -0.339212    0.0
6  0.385030 -0.278698    0.0
7 -2.294758  1.775379    0.0

In [313]: df3.dtypes
Out[313]: 
A    float32
B    float64
C    float64
dtype: object

Атрибут values в DataFrame возвращает *наименьший общий знаменатель* типов данных, что означает тип данных, который может вместить **ВСЕ** типы данных в результирующем однородном массиве numpy. Это может привести к некоторым *преобразованиям типов*.

In [314]: df3.values.dtype
Out[314]: dtype('float64')

astype

Вы можете использовать метод astype() для явного преобразования типов данных из одного в другой. По умолчанию они возвращают копию, даже если тип данных не изменился (передайте copy=False для изменения этого поведения). Кроме того, они вызовут исключение, если операция astype некорректна.

Преобразование типов всегда происходит в соответствии с правилами **numpy**. Если в операции участвуют два разных типа данных, то более *общий* из них будет использоваться в качестве результата операции.

In [315]: df3
Out[315]: 
          A         B      C
0  0.675238  0.296947    0.0
1 -0.327398  0.007045  255.0
2  2.025163  0.707877    1.0
3 -1.998126  0.950661    0.0
4 -1.631068  0.087527    0.0
5  1.737299 -0.339212    0.0
6  0.385030 -0.278698    0.0
7 -2.294758  1.775379    0.0

In [316]: df3.dtypes
Out[316]: 
A    float32
B    float64
C    float64
dtype: object

# conversion of dtypes
In [317]: df3.astype('float32').dtypes
Out[317]: 
A    float32
B    float32
C    float32
dtype: object

Преобразовать подмножество столбцов в указанный тип с помощью astype()

In [318]: dft = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6], 'c': [7, 8, 9]})

In [319]: dft[['a','b']] = dft[['a','b']].astype(np.uint8)

In [320]: dft
Out[320]: 
   a  b  c
0  1  4  7
1  2  5  8
2  3  6  9

In [321]: dft.dtypes
Out[321]: 
a    uint8
b    uint8
c    int64
dtype: object

Примечание

При попытке преобразовать подмножество столбцов в указанный тип с помощью astype() и loc() происходит расширение типов.

loc() пытается вписать то, что мы присваиваем, в текущие типы данных, в то время как [] перезапишет их, взяв тип данных из правой части. Поэтому следующий фрагмент кода даёт нежелательный результат.

In [322]: dft = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6], 'c': [7, 8, 9]})

In [323]: dft.loc[:, ['a', 'b']].astype(np.uint8).dtypes
Out[323]: 
a    uint8
b    uint8
dtype: object

In [324]: dft.loc[:, ['a', 'b']] = dft.loc[:, ['a', 'b']].astype(np.uint8)

In [325]: dft.dtypes
Out[325]: 
a    int64
b    int64
c    int64
dtype: object

Преобразование объектов

pandas предлагает различные функции для попытки принудительного преобразования типов из типа данных object в другие типы. Следующие функции доступны для одномерных массивов объектов или скаляров:

  • to_numeric() (преобразование в числовые типы данных)

    In [326]: m = ['1.1', 2, 3]
    
    In [327]: pd.to_numeric(m)
    Out[327]: array([ 1.1,  2. ,  3. ])
    
  • to_datetime() (преобразование в объекты datetime)

    In [328]: import datetime
    
    In [329]: m = ['2016-07-09', datetime.datetime(2016, 3, 2)]
    
    In [330]: pd.to_datetime(m)
    Out[330]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
    
  • to_timedelta() (преобразование в объекты timedelta)

    In [331]: m = ['5us', pd.Timedelta('1day')]
    
    In [332]: pd.to_timedelta(m)
    Out[332]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
    

Для принудительного преобразования можно передать аргумент errors, который указывает, как pandas должен обрабатывать элементы, которые не могут быть преобразованы в желаемый тип данных или объект. По умолчанию, errors='raise', что означает, что любые ошибки, возникшие во время процесса преобразования, будут подняты. Однако, если errors='coerce', эти ошибки будут проигнорированы, и pandas преобразует проблемные элементы в pd.NaT (для datetime и timedelta) или np.nan (для числовых). Это может быть полезно, если вы читаете данные, которые в основном имеют желаемый тип данных (например, числовой, datetime), но иногда содержат несовместимые элементы, которые вы хотите представить как пропущенные значения:

In [333]: import datetime

In [334]: m = ['apple', datetime.datetime(2016, 3, 2)]

In [335]: pd.to_datetime(m, errors='coerce')
Out[335]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)

In [336]: m = ['apple', 2, 3]

In [337]: pd.to_numeric(m, errors='coerce')
Out[337]: array([ nan,   2.,   3.])

In [338]: m = ['apple', pd.Timedelta('1day')]

In [339]: pd.to_timedelta(m, errors='coerce')
Out[339]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)

Параметр errors имеет третью опцию errors='ignore', которая просто вернёт переданные данные, если при преобразовании в нужный тип данных возникнут ошибки:

In [340]: import datetime

In [341]: m = ['apple', datetime.datetime(2016, 3, 2)]

In [342]: pd.to_datetime(m, errors='ignore')
Out[342]: array(['apple', datetime.datetime(2016, 3, 2, 0, 0)], dtype=object)

In [343]: m = ['apple', 2, 3]

In [344]: pd.to_numeric(m, errors='ignore')
Out[344]: array(['apple', 2, 3], dtype=object)

In [345]: m = ['apple', pd.Timedelta('1day')]

In [346]: pd.to_timedelta(m, errors='ignore')
Out[346]: array(['apple', Timedelta('1 days 00:00:00')], dtype=object)

В дополнение к преобразованию объектов, to_numeric() предоставляет ещё один аргумент downcast, который даёт возможность понизить разрядность только что (или уже) числовых данных до меньшего типа, что может экономить память:

In [347]: m = ['1', 2, 3]

In [348]: pd.to_numeric(m, downcast='integer')   # smallest signed int dtype
Out[348]: array([1, 2, 3], dtype=int8)

In [349]: pd.to_numeric(m, downcast='signed')    # same as 'integer'
Out[349]: array([1, 2, 3], dtype=int8)

In [350]: pd.to_numeric(m, downcast='unsigned')  # smallest unsigned int dtype
Out[350]: array([1, 2, 3], dtype=uint8)

In [351]: pd.to_numeric(m, downcast='float')     # smallest float dtype
Out[351]: array([ 1.,  2.,  3.], dtype=float32)

Поскольку эти методы применяются только к одномерным массивам, спискам или скалярам, они не могут быть использованы непосредственно для многомерных объектов, таких как DataFrames. Однако с помощью apply() мы можем эффективно «применить» функцию к каждому столбцу:

In [352]: import datetime

In [353]: df = pd.DataFrame([['2016-07-09', datetime.datetime(2016, 3, 2)]] * 2, dtype='O')

In [354]: df
Out[354]: 
            0                    1
0  2016-07-09  2016-03-02 00:00:00
1  2016-07-09  2016-03-02 00:00:00

In [355]: df.apply(pd.to_datetime)
Out[355]: 
           0          1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02

In [356]: df = pd.DataFrame([['1.1', 2, 3]] * 2, dtype='O')

In [357]: df
Out[357]: 
     0  1  2
0  1.1  2  3
1  1.1  2  3

In [358]: df.apply(pd.to_numeric)
Out[358]: 
     0  1  2
0  1.1  2  3
1  1.1  2  3

In [359]: df = pd.DataFrame([['5us', pd.Timedelta('1day')]] * 2, dtype='O')

In [360]: df
Out[360]: 
     0                1
0  5us  1 days 00:00:00
1  5us  1 days 00:00:00

In [361]: df.apply(pd.to_timedelta)
Out[361]: 
                0      1
0 00:00:00.000005 1 days
1 00:00:00.000005 1 days

Особенности

Выполнение операций выбора на данных типа integer может легко привести к расширению данных до floating. Тип данных входных данных сохранится в случаях, когда nans не вводятся (начиная с 0.11.0). См. также Особенности с целочисленными пропущенными значениями

In [362]: dfi = df3.astype('int32')

In [363]: dfi['E'] = 1

In [364]: dfi
Out[364]: 
   A  B    C  E
0  0  0    0  1
1  0  0  255  1
2  2  0    1  1
3 -1  0    0  1
4 -1  0    0  1
5  1  0    0  1
6  0  0    0  1
7 -2  1    0  1

In [365]: dfi.dtypes
Out[365]: 
A    int32
B    int32
C    int32
E    int64
dtype: object

In [366]: casted = dfi[dfi>0]

In [367]: casted
Out[367]: 
     A    B      C  E
0  NaN  NaN    NaN  1
1  NaN  NaN  255.0  1
2  2.0  NaN    1.0  1
3  NaN  NaN    NaN  1
4  NaN  NaN    NaN  1
5  1.0  NaN    NaN  1
6  NaN  NaN    NaN  1
7  NaN  1.0    NaN  1

In [368]: casted.dtypes
Out[368]: 
A    float64
B    float64
C    float64
E      int64
dtype: object

В то время как типы данных float останутся неизменными.

In [369]: dfa = df3.copy()

In [370]: dfa['A'] = dfa['A'].astype('float32')

In [371]: dfa.dtypes
Out[371]: 
A    float32
B    float64
C    float64
dtype: object

In [372]: casted = dfa[df2>0]

In [373]: casted
Out[373]: 
          A         B      C
0  0.675238  0.296947    NaN
1       NaN  0.007045  255.0
2  2.025163  0.707877    1.0
3       NaN  0.950661    NaN
4       NaN  0.087527    NaN
5       NaN       NaN    NaN
6  0.385030       NaN    NaN
7 -2.294758  1.775379    NaN

In [374]: casted.dtypes
Out[374]: 
A    float32
B    float64
C    float64
dtype: object

Выбор столбцов на основе типа данных

Новое в версии 0.14.1.

Метод select_dtypes() реализует подмножество столбцов на основе их типов данных.

Сначала давайте создадим DataFrame с различными типами данных:

In [375]: df = pd.DataFrame({'string': list('abc'),
   .....:                    'int64': list(range(1, 4)),
   .....:                    'uint8': np.arange(3, 6).astype('u1'),
   .....:                    'float64': np.arange(4.0, 7.0),
   .....:                    'bool1': [True, False, True],
   .....:                    'bool2': [False, True, False],
   .....:                    'dates': pd.date_range('now', periods=3).values,
   .....:                    'category': pd.Series(list("ABC")).astype('category')})
   .....: 

In [376]: df['tdeltas'] = df.dates.diff()

In [377]: df['uint64'] = np.arange(3, 6).astype('u8')

In [378]: df['other_dates'] = pd.date_range('20130101', periods=3).values

In [379]: df['tz_aware_dates'] = pd.date_range('20130101', periods=3, tz='US/Eastern')

In [380]: df
Out[380]: 
   bool1  bool2 category                      dates  float64  int64 string  \
0   True  False        A 2016-12-24 18:31:36.297875      4.0      1      a   
1  False   True        B 2016-12-25 18:31:36.297875      5.0      2      b   
2   True  False        C 2016-12-26 18:31:36.297875      6.0      3      c   

   uint8  tdeltas  uint64 other_dates            tz_aware_dates  
0      3      NaT       3  2013-01-01 2013-01-01 00:00:00-05:00  
1      4   1 days       4  2013-01-02 2013-01-02 00:00:00-05:00  
2      5   1 days       5  2013-01-03 2013-01-03 00:00:00-05:00  

И типы данных

In [381]: df.dtypes
Out[381]: 
bool1                                   bool
bool2                                   bool
category                            category
dates                         datetime64[ns]
float64                              float64
int64                                  int64
string                                object
uint8                                  uint8
tdeltas                      timedelta64[ns]
uint64                                uint64
other_dates                   datetime64[ns]
tz_aware_dates    datetime64[ns, US/Eastern]
dtype: object

select_dtypes() имеет два параметра include и exclude, которые позволяют вам сказать «дайте мне столбцы С этими типами данных» (include) и/или «дайте столбцы БЕЗ этих типов данных» (exclude).

Например, для выбора bool столбцов

In [382]: df.select_dtypes(include=[bool])
Out[382]: 
   bool1  bool2
0   True  False
1  False   True
2   True  False

Вы также можете передать имя типа данных в иерархии типов данных NumPy:

In [383]: df.select_dtypes(include=['bool'])
Out[383]: 
   bool1  bool2
0   True  False
1  False   True
2   True  False

select_dtypes() также работает с общими типами данных.

Например, для выбора всех числовых и логических столбцов, исключая беззнаковые целые числа

In [384]: df.select_dtypes(include=['number', 'bool'], exclude=['unsignedinteger'])
Out[384]: 
   bool1  bool2  float64  int64  tdeltas
0   True  False      4.0      1      NaT
1  False   True      5.0      2   1 days
2   True  False      6.0      3   1 days

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

In [385]: df.select_dtypes(include=['object'])
Out[385]: 
  string
0      a
1      b
2      c

Чтобы увидеть все дочерние типы данных общего типа dtype , например, numpy.number, вы можете определить функцию, которая возвращает дерево дочерних типов данных:

In [386]: def subdtypes(dtype):
   .....:     subs = dtype.__subclasses__()
   .....:     if not subs:
   .....:         return dtype
   .....:     return [dtype, [subdtypes(dt) for dt in subs]]
   .....: 

Все типы данных NumPy являются подклассами numpy.generic:

In [387]: subdtypes(np.generic)
Out[387]: 
[numpy.generic,
 [[numpy.number,
   [[numpy.integer,
     [[numpy.signedinteger,
       [numpy.int8,
        numpy.int16,
        numpy.int32,
        numpy.int64,
        numpy.int64,
        numpy.timedelta64]],
      [numpy.unsignedinteger,
       [numpy.uint8,
        numpy.uint16,
        numpy.uint32,
        numpy.uint64,
        numpy.uint64]]]],
    [numpy.inexact,
     [[numpy.floating,
       [numpy.float16, numpy.float32, numpy.float64, numpy.float128]],
      [numpy.complexfloating,
       [numpy.complex64, numpy.complex128, numpy.complex256]]]]]],
  [numpy.flexible,
   [[numpy.character, [numpy.string_, numpy.unicode_]],
    [numpy.void, [numpy.record]]]],
  numpy.bool_,
  numpy.datetime64,
  numpy.object_]]

Примечание

Pandas также определяет типы category, и datetime64[ns, tz], которые не интегрированы в обычную иерархию NumPy и не будут отображаться с помощью вышеупомянутой функции.

Примечание

Параметры include и exclude должны быть последовательностями, не являющимися строками.

© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.19.2/basics.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API