Основные базовые функции
Здесь обсуждаются многие базовые функции, общие для структур данных 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, по строкам или столбцам, или по элементам.
-
Применение функции к всей таблице:
pipe() -
Применение функции по строкам или столбцам:
apply() -
Применение функции к каждому элементу:
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(), которые возвращают наименьшие или наибольшие значения. Для большого
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