Spec-Zone.ru › pandas 0.18

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

Здесь обсуждаются многие базовые функции, общие для структур данных 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 не имеют одного и того же типа dtype) это не будет выполняться. В отличие от меток осей, атрибуту values нельзя присвоить значение.

Примечание

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

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

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

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

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

Операция 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 и DataFrame (хотя пока не в Panel) арифметические функции имеют возможность ввода fill_value, т. е. значения, которое необходимо подставить, когда отсутствует не более одного значения в определённом месте. Например, при сложении двух объектов DataFrame вы можете задать, что NaN следует обрабатывать как 0, если отсутствуют оба значения DataFrame, в таком случае результатом будет NaN (позже вы можете заменить NaN другим значением, используя fillna, если это необходимо).

In [28]: df
Out[28]: 
        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 [29]: df2
Out[29]: 
        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 [30]: df + df2
Out[30]: 
        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 [31]: df.add(df2, fill_value=0)
Out[31]: 
        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

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

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

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

In [33]: df2.ne(df)
Out[33]: 
     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 [34]: (df > 0).all()
Out[34]: 
one      False
three    False
two      False
dtype: bool

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

Вы можете сократить до конечного логического значения.

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

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

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

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

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

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

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

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

In [42]: pd.DataFrame([[False]]).bool()
Out[42]: 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 [43]: df+df == df*2
Out[43]: 
     one  three   two
a   True  False  True
b   True   True  True
c   True   True  True
d  False   True  True

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

In [54]: pd.Series(['foo', 'bar', 'baz']) == np.array(['foo', 'bar', 'qux'])
Out[54]: 
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 [55]: np.array([1, 2, 3]) == np.array([2])
Out[55]: array([False,  True, False], dtype=bool)

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

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

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

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

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

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

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

In [60]: df2
Out[60]: 
     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 [61]: df1.combine_first(df2)
Out[61]: 
     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 [62]: combiner = lambda x, y: np.where(pd.isnull(x), y, x)

In [63]: df1.combine(df2, combiner)
Out[63]: 
     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 [64]: df
Out[64]: 
        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 [65]: df.mean(0)
Out[65]: 
one     -0.251274
three    0.469799
two     -0.191421
dtype: float64

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

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

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

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

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

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

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

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

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

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

In [73]: df.cumsum()
Out[73]: 
        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 Выборовый коэффициент асимметрии (третий момент)
kurt Выборовый коэффициент эксцесса (четвертый момент)
quantile Выборовый квантиль (значение в %)
cumsum Кумулятивная сумма
cumprod Кумулятивное произведение
cummax Кумулятивный максимум
cummin Кумулятивный минимум

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

In [74]: np.mean(df['one'])
Out[74]: -0.25127365175839511

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

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

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

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

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

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

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

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

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

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

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

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

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

In [85]: frame.describe()
Out[85]: 
                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%           NaN         NaN         NaN         NaN         NaN
50%           NaN         NaN         NaN         NaN         NaN
75%           NaN         NaN         NaN         NaN         NaN
max      3.007143    2.627688    2.702490    2.850852    3.072117

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

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

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

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

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

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

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

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

In [90]: frame.describe()
Out[90]: 
              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 [91]: frame.describe(include=['object'])
Out[91]: 
         a
count    4
unique   2
top     No
freq     2

In [92]: frame.describe(include=['number'])
Out[92]: 
              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 [93]: frame.describe(include='all')
Out[93]: 
          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 [94]: s1 = pd.Series(np.random.randn(5))

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

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

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

In [98]: df1
Out[98]: 
          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 [99]: df1.idxmin(axis=0)
Out[99]: 
A    2
B    0
C    1
dtype: int64

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

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

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

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

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

Примечание

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

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

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

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

In [105]: data
Out[105]: 
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 [106]: s = pd.Series(data)

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

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

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

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

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

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

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

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

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

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

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

In [115]: factor
Out[115]: 
[(-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 [116]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])

In [117]: factor
Out[117]: 
[(-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 [118]: arr = np.random.randn(30)

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

In [120]: factor
Out[120]: 
[(-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 [121]: pd.value_counts(factor)
Out[121]: 
(1.00736, 1.976]     8
[-1.0705, -0.439]    8
(-0.139, 1.00736]    7
(-0.439, -0.139]     7
dtype: int64

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

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

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

In [124]: factor
Out[124]: 
[(-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 [125]: import statsmodels.formula.api as sm

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

In [127]: (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[127]: 
<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:                Tue, 03 May 2016   Pseudo R-squ.:                  0.6878
Time:                        09:03:28   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, которые представили популярный (%>%) (читать pipe) оператор для R. Реализация pipe здесь довольно чистая и органично вписывается в python. Мы рекомендуем вам изучить исходный код (pd.DataFrame.pipe?? в IPython).

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

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

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

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

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

In [131]: df.apply(np.cumsum)
Out[131]: 
        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 [132]: df.apply(np.exp)
Out[132]: 
        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 [133]: tsdf = pd.DataFrame(np.random.randn(1000, 3), columns=['A', 'B', 'C'],
   .....:                     index=pd.date_range('1/1/2000', periods=1000))
   .....: 

In [134]: tsdf.apply(lambda x: x.idxmax())
Out[134]: 
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 [135]: tsdf
Out[135]: 
                   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 [136]: tsdf.apply(pd.Series.interpolate)
Out[136]: 
                   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 [137]: df4
Out[137]: 
        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 [138]: f = lambda x: len(str(x))

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

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

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

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

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

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

In [144]: s.map(t)
Out[144]: 
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 [145]: import pandas.util.testing as tm

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

In [147]: panel
Out[147]: 
<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 [148]: panel['ItemA']
Out[148]: 
                   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 [149]: result = panel.apply(lambda x: x*2, axis='items')

In [150]: result
Out[150]: 
<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 [151]: result['ItemA']
Out[151]: 
                   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 [152]: panel.apply(lambda x: x.dtype, axis='items')
Out[152]: 
                  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 [153]: panel.apply(lambda x: x.sum(), axis='major_axis')
Out[153]: 
      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 [154]: panel.sum('major_axis')
Out[154]: 
      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 [155]: result = panel.apply(
   .....:            lambda x: (x-x.mean())/x.std(),
   .....:            axis='major_axis')
   .....: 

In [156]: result
Out[156]: 
<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 [157]: result['ItemA']
Out[157]: 
                   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 [158]: f = lambda x: ((x.T-x.mean(1))/x.std(1)).T

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

In [160]: result
Out[160]: 
<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 [161]: result.loc[:,:,'ItemA']
Out[161]: 
                   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 [162]: result = pd.Panel(dict([ (ax, f(panel.loc[:,:,ax]))
   .....:                         for ax in panel.minor_axis ]))
   .....: 

In [163]: result
Out[163]: 
<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 [164]: result.loc[:,:,'ItemA']
Out[164]: 
                   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 [165]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

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

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

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

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

In [168]: df
Out[168]: 
        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 [169]: df.reindex(index=['c', 'f', 'b'], columns=['three', 'two', 'one'])
Out[169]: 
      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 [170]: rs = s.reindex(df.index)

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

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

Это означает, что индекс переиндексированной Series является тем же объектом Python, что и индекс DataFrame.

См. также

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

Примечание

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

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

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

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

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

In [175]: df.reindex_like(df2)
Out[175]: 
        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 [176]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

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

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

In [179]: s1.align(s2)
Out[179]: 
(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 [180]: s1.align(s2, join='inner')
Out[180]: 
(b    1.092702
 c   -1.481449
 d    1.781190
 dtype: float64, b    1.092702
 c   -1.481449
 d    1.781190
 dtype: float64)

In [181]: s1.align(s2, join='left')
Out[181]: 
(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)

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

In [182]: df.align(df2, join='inner')
Out[182]: 
(        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 [183]: df.align(df2, join='inner', axis=0)
Out[183]: 
(        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 [184]: df.align(df2.ix[0], axis=1)
Out[184]: 
(        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 [185]: rng = pd.date_range('1/3/2000', periods=8)

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

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

In [188]: ts
Out[188]: 
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 [189]: ts2
Out[189]: 
2000-01-03    0.480993
2000-01-06    1.990533
2000-01-09   -2.927808
dtype: float64

In [190]: ts2.reindex(ts.index)
Out[190]: 
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 [191]: ts2.reindex(ts.index, method='ffill')
Out[191]: 
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 [192]: ts2.reindex(ts.index, method='bfill')
Out[192]: 
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 [193]: ts2.reindex(ts.index, method='nearest')
Out[193]: 
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 [194]: ts2.reindex(ts.index).fillna(method='ffill')
Out[194]: 
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 [195]: ts2.reindex(ts.index, method='ffill', limit=1)
Out[195]: 
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 [196]: ts2.reindex(ts.index, method='ffill', tolerance='1 day')
Out[196]: 
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 если это возможно. Это позволяет указывать допуск с помощью соответствующих строк.

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

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

In [197]: df
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

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

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

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

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

Переименование/отображение меток

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

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

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

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

In [203]: df.rename(columns={'one' : 'foo', 'two' : 'bar'},
   .....:           index={'a' : 'apple', 'b' : 'banana', 'd' : 'durian'})
   .....: 
Out[203]: 
             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 [204]: s.rename("scalar-name")
Out[204]: 
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, следуют соглашению dict-like, итерации по «ключам» объектов.

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

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

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

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

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

Объекты pandas также имеют метод dict-like iteritems() для итерации по парам (ключ, значение).

Для итерации по строкам DataFrame вы можете использовать следующие методы:

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

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

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

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

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

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

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

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

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

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

iteritems

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

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

Например:

In [210]: 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 [211]: 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 [212]: df_orig = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])

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

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

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

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

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

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

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

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

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

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

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

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

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

itertuples

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

Например,

In [223]: 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, а просто возвращает значения внутри именованного кортежа. Следовательно, itertuples() сохраняет тип данных значений и, как правило, быстрее, чем iterrows().

Примечание

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

.dt accessor

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

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

In [225]: s
Out[225]: 
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 [226]: s.dt.hour
Out[226]: 
0    9
1    9
2    9
3    9
dtype: int64

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

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

Это позволяет использовать такие выражения:

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

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

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

In [231]: stz
Out[231]: 
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 [232]: stz.dt.tz
Out[232]: <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>

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

In [233]: s.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[233]: 
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 [234]: s = pd.Series(pd.date_range('20130101', periods=4))

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

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

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

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

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

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

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

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

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

In [245]: s
Out[245]: 
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 [246]: s.dt.days
Out[246]: 
0    1
1    1
2    1
3    1
dtype: int64

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

In [248]: s.dt.components
Out[248]: 
   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 [249]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])

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

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

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

Сортировка

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

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

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

По индексу

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

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

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

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

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

# Series
In [255]: unsorted_df['three'].sort_index()
Out[255]: 
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 [256]: df1 = pd.DataFrame({'one':[2,1,1,1],'two':[1,3,2,4],'three':[5,4,3,2]})

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

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

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

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

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

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

In [261]: s.sort_values(na_position='first')
Out[261]: 
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 [262]: ser = pd.Series([1, 2, 3])

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

In [274]: 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 [275]: df.nlargest(3, 'a')
Out[275]: 
    a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN

In [276]: df.nlargest(5, ['a', 'c'])
Out[276]: 
    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

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

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

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

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

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

In [280]: df1.sort_values(by=('a','two'))
Out[280]: 
    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 или values
  • Для однородных данных, непосредственное изменение значений через атрибут values или расширенный индексирование

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

Типы данных

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

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

In [281]: 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 [282]: dft
Out[282]: 
          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 [283]: dft.dtypes
Out[283]: 
A           float64
B             int64
C            object
D    datetime64[ns]
E           float32
F              bool
G              int8
dtype: object

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

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

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

# these ints are coerced to floats
In [285]: pd.Series([1, 2, 3, 4, 5, 6.])
Out[285]: 
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 [286]: pd.Series([1, 2, 3, 6., 'foo'])
Out[286]: 
0      1
1      2
2      3
3      6
4    foo
dtype: object

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

In [287]: dft.get_dtype_counts()
Out[287]: 
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 [288]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=['A'], dtype='float32')

In [289]: df1
Out[289]: 
          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 [290]: df1.dtypes
Out[290]: 
A    float32
dtype: object

In [291]: 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 [292]: df2
Out[292]: 
          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 [293]: df2.dtypes
Out[293]: 
A    float16
B    float64
C      uint8
dtype: object

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

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

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

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

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

Однако Numpy выберет зависимые от платформы типы при создании массивов. В следующих случаях будет получен тип int32 на 32-битной платформе.

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

Преобразование к более широкому типу

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

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

In [299]: df3
Out[299]: 
          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 [300]: df3.dtypes
Out[300]: 
A    float32
B    float64
C    float64
dtype: object

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

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

astype

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

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

In [302]: df3
Out[302]: 
          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 [303]: df3.dtypes
Out[303]: 
A    float32
B    float64
C    float64
dtype: object

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

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

convert_objects() — это метод для попытки принудительного преобразования типов из типа object в другие типы. Для принудительного преобразования определённых типов, которые являются похожими на числа, например, строка, представляющая число, передайте convert_numeric=True. Это принудит строки и числа быть числами, если это возможно; в противном случае они будут установлены как np.nan.

In [305]: df3['D'] = '1.'

In [306]: df3['E'] = '1'

In [307]: df3.convert_objects(convert_numeric=True).dtypes
Out[307]: 
A    float32
B    float64
C    float64
D    float64
E      int64
dtype: object

# same, but specific dtype conversion
In [308]: df3['D'] = df3['D'].astype('float16')

In [309]: df3['E'] = df3['E'].astype('int32')

In [310]: df3.dtypes
Out[310]: 
A    float32
B    float64
C    float64
D    float16
E      int32
dtype: object

Чтобы принудительно преобразовать в datetime64[ns], передайте convert_dates='coerce'. Это преобразует любой объект типа datetime в даты, заставив другие значения стать NaT. Это может быть полезно, если вы читаете данные, в которых в основном содержатся даты, но иногда встречаются значения, которые не являются датами, и вы хотите представить их как пропущенные.

In [311]: import datetime

In [312]: s = pd.Series([datetime.datetime(2001,1,1,0,0),
   .....:               'foo', 1.0, 1, pd.Timestamp('20010104'),
   .....:               '20010105'], dtype='O')
   .....: 

In [313]: s
Out[313]: 
0    2001-01-01 00:00:00
1                    foo
2                      1
3                      1
4    2001-01-04 00:00:00
5               20010105
dtype: object

In [314]: pd.to_datetime(s, errors='coerce')
Out[314]: 
0   2001-01-01
1          NaT
2          NaT
3          NaT
4   2001-01-04
5   2001-01-05
dtype: datetime64[ns]

Кроме того, convert_objects() будет пытаться выполнить мягкое преобразование всех типов object, то есть если все объекты в Series имеют одинаковый тип, Series будет иметь этот тип.

Особенности

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

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

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

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

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

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

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

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

В то время как типы данных с плавающей точкой останутся неизменными.

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

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

In [324]: dfa.dtypes
Out[324]: 
A    float32
B    float64
C    float64
D    float16
E      int32
dtype: object

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

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

In [327]: casted.dtypes
Out[327]: 
A    float32
B    float64
C    float64
D    float16
E    float64
dtype: object

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

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

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

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

In [328]: 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 [329]: df['tdeltas'] = df.dates.diff()

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

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

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

In [333]: df
Out[333]: 
   bool1  bool2 category                      dates  float64  int64 string  \
0   True  False        A 2016-05-03 09:03:31.005096      4.0      1      a   
1  False   True        B 2016-05-04 09:03:31.005096      5.0      2      b   
2   True  False        C 2016-05-05 09:03:31.005096      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 [334]: df.dtypes
Out[334]: 
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 [335]: df.select_dtypes(include=[bool])
Out[335]: 
   bool1  bool2
0   True  False
1  False   True
2   True  False

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

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

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

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

In [337]: df.select_dtypes(include=['number', 'bool'], exclude=['unsignedinteger'])
Out[337]: 
   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 [338]: df.select_dtypes(include=['object'])
Out[338]: 
  string
0      a
1      b
2      c

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

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

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

In [340]: subdtypes(np.generic)
Out[340]: 
[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.18.1/basics.html

Spec-Zone.ru

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