Введение в структуры данных
Мы начнём с быстрого, но неполного обзора основных структур данных в pandas, чтобы вы могли начать работу. Основное поведение относительно типов данных, индексирования и маркировки/выравнивания осей применимо ко всем объектам. Для начала импортируйте numpy и загрузите pandas в ваше пространство имён:
In [1]: import numpy as np In [2]: import pandas as pd
Вот основное правило, которое следует иметь в виду: выравнивание данных является внутренним. Связь между метками и данными не будет разорвана, если вы этого явно не сделаете.
Мы дадим краткое введение в структуры данных, а затем рассмотрим все основные категории функциональности и методов в отдельных разделах.
Ряд
Series — это одномерный маркированный массив, способный хранить любые типы данных (целые числа, строки, числа с плавающей точкой, объекты Python и т. д.). Метки осей в совокупности называются индексом. Основной метод создания ряда — вызов:
>>> s = pd.Series(data, index=index)
Здесь data может быть многими различными вещами:
- словарь Python
- массив ndarray
- скалярное значение (например, 5)
Переданный индекс — это список меток осей. Таким образом, это разделяется на несколько случаев в зависимости от того, что представляют собой данные:
Из массива ndarray
Если data является массивом ndarray, индекс должен иметь такую же длину, что и данные. Если индекс не указан, он будет создан со значениями [0, ..., len(data) - 1].
In [3]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e']) In [4]: s Out[4]: a 0.2735 b 0.6052 c -0.1692 d 1.8298 e 0.5432 dtype: float64 In [5]: s.index Out[5]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object') In [6]: pd.Series(np.random.randn(5)) Out[6]: 0 0.3674 1 -0.8230 2 -1.0295 3 -1.0523 4 -0.8502 dtype: float64
Примечание
Начиная с версии v0.8.0, pandas поддерживает не уникальные значения индекса. Если будет попытка операции, которая не поддерживает дубликаты значений индекса, в этот момент будет выброшено исключение. Причина, по которой это делается по умолчанию, почти полностью связана с производительностью (есть много случаев в вычислениях, например, в части GroupBy, где индекс не используется).
Из словаря dict
Если data является словарем dict, если индекс указан, будут извлечены значения в данных, соответствующие меткам в индексе. В противном случае индекс будет построен из отсортированных ключей словаря, если это возможно.
In [7]: d = {'a' : 0., 'b' : 1., 'c' : 2.}
In [8]: pd.Series(d)
Out[8]:
a 0.0
b 1.0
c 2.0
dtype: float64
In [9]: pd.Series(d, index=['b', 'c', 'd', 'a'])
Out[9]:
b 1.0
c 2.0
d NaN
a 0.0
dtype: float64
Примечание
NaN (не число) является стандартным маркером отсутствующих данных, используемым в pandas
Из скалярного значения Если data является скалярным значением, должен быть указан индекс. Значение будет повторяться для соответствия длине индекса
In [10]: pd.Series(5., index=['a', 'b', 'c', 'd', 'e']) Out[10]: a 5.0 b 5.0 c 5.0 d 5.0 e 5.0 dtype: float64
Ряд похож на ndarray
Series ведет себя очень похоже на ndarray, и является допустимым аргументом для большинства функций NumPy. Однако такие вещи, как срезы, также производят срез индекса.
In [11]: s[0] Out[11]: 0.27348116325673794 In [12]: s[:3] Out[12]: a 0.2735 b 0.6052 c -0.1692 dtype: float64 In [13]: s[s > s.median()] Out[13]: b 0.6052 d 1.8298 dtype: float64 In [14]: s[[4, 3, 1]] Out[14]: e 0.5432 d 1.8298 b 0.6052 dtype: float64 In [15]: np.exp(s) Out[15]: a 1.3145 b 1.8317 c 0.8443 d 6.2327 e 1.7215 dtype: float64
Мы рассмотрим индексирование на основе массива в отдельном разделе.
Ряд похож на словарь
Ряд похож на словарь фиксированного размера, так как вы можете получить и установить значения по метке индекса:
In [16]: s['a'] Out[16]: 0.27348116325673794 In [17]: s['e'] = 12. In [18]: s Out[18]: a 0.2735 b 0.6052 c -0.1692 d 1.8298 e 12.0000 dtype: float64 In [19]: 'e' in s Out[19]: True In [20]: 'f' in s Out[20]: False
Если метка не содержится, возникает исключение:
>>> s['f'] KeyError: 'f'
Используя метод get, отсутствующая метка вернёт None или заданный по умолчанию:
In [21]: s.get('f')
In [22]: s.get('f', np.nan)
Out[22]: nan
См. также раздел об обращении к атрибутам.
Векторные операции и выравнивание меток со строками
При выполнении анализа данных, как и с обычными массивами NumPy, итерация по значениям ряда обычно не требуется. Ряд также может быть передан в большинство методов NumPy, ожидающих массив ndarray.
In [23]: s + s Out[23]: a 0.5470 b 1.2104 c -0.3385 d 3.6596 e 24.0000 dtype: float64 In [24]: s * 2 Out[24]: a 0.5470 b 1.2104 c -0.3385 d 3.6596 e 24.0000 dtype: float64 In [25]: np.exp(s) Out[25]: a 1.3145 b 1.8317 c 0.8443 d 6.2327 e 162754.7914 dtype: float64
Ключевое отличие между рядом и массивом ndarray заключается в том, что операции между рядами автоматически выравнивают данные на основе метки. Таким образом, вы можете записать вычисления, не задумываясь о том, имеют ли вовлечённые ряды одинаковые метки.
In [26]: s[1:] + s[:-1] Out[26]: a NaN b 1.2104 c -0.3385 d 3.6596 e NaN dtype: float64
Результат операции между невыровненными рядами будет иметь объединение участвующих индексов. Если метка не найдена в одном ряду или другом, результат будет помечен как отсутствующий NaN. Возможность писать код без явного выравнивания данных предоставляет огромную свободу и гибкость при интерактивном анализе и исследовании данных. Интегрированные функции выравнивания данных в структурах данных pandas отличают pandas от большинства аналогичных инструментов для работы с маркированными данными.
Примечание
В общем случае мы решили сделать по умолчанию результат операций между объектами с разными индексами, чтобы получить объединение индексов, чтобы избежать потери информации. Иметь метку индекса, даже если данные отсутствуют, обычно является важной информацией в рамках вычислений. Конечно, у вас есть возможность удалить метки с отсутствующими данными с помощью функции dropna.
Атрибут Name
Ряды также могут иметь атрибут name:
In [27]: s = pd.Series(np.random.randn(5), name='something') In [28]: s Out[28]: 0 1.5140 1 -1.2345 2 0.5666 3 -1.0184 4 0.1081 Name: something, dtype: float64 In [29]: s.name Out[29]: 'something'
Ряд name будет назначен автоматически во многих случаях, особенно при извлечении 1D срезов DataFrame, как вы увидите ниже.
Новое в версии 0.18.0.
Вы можете переименовать ряд с помощью метода pandas.Series.rename().
In [30]: s2 = s.rename("different")
In [31]: s2.name
Out[31]: 'different'
Обратите внимание, что s и s2 относятся к различным объектам.
Таблица DataFrame
DataFrame — это двумерная маркированная структура данных со столбцами, потенциально разных типов. Вы можете представить её как электронную таблицу или таблицу SQL, или словарь объектов Series. Это, как правило, наиболее часто используемый объект pandas. Как и Series, DataFrame принимает множество различных типов входных данных:
- Словарь 1D ndarray, списков, словарей или Series
- 2-мерный numpy.ndarray
- Структурированный или записывающий ndarray
Series- Другой
DataFrame
Вместе с данными вы можете необязательно передать аргументы index (метки строк) и columns (метки столбцов). Если вы передадите индекс и/или столбцы, вы гарантируете индекс и/или столбцы результирующего DataFrame. Таким образом, словарь Series плюс конкретный индекс отбросит все данные, не соответствующие переданному индексу.
Если метки осей не указаны, они будут построены из входных данных на основе общепринятых правил.
Из словаря Series или словарей
Результирующий индекс будет объединением индексов различных Series. Если есть вложенные словари, они будут сначала преобразованы в Series. Если столбцы не переданы, столбцы будут отсортированным списком ключей словаря.
In [32]: d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
....: 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
....:
In [33]: df = pd.DataFrame(d)
In [34]: df
Out[34]:
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
In [35]: pd.DataFrame(d, index=['d', 'b', 'a'])
Out[35]:
one two
d NaN 4.0
b 2.0 2.0
a 1.0 1.0
In [36]: pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
Out[36]:
two three
d 4.0 NaN
b 2.0 NaN
a 1.0 NaN
Метки строк и столбцов можно получить соответственно, обратившись к атрибутам index и columns:
Примечание
Когда конкретный набор столбцов передаётся вместе со словарем данных, передаваемые столбцы переопределяют ключи в словаре.
In [37]: df.index Out[37]: Index([u'a', u'b', u'c', u'd'], dtype='object') In [38]: df.columns Out[38]: Index([u'one', u'two'], dtype='object')
Из словаря ndarray/списков
Массивы ndarray должны иметь одинаковую длину. Если указан индекс, его длина также должна совпадать с длиной массивов. Если индекс не указан, результат будет range(n), где n — длина массива.
In [39]: d = {'one' : [1., 2., 3., 4.],
....: 'two' : [4., 3., 2., 1.]}
....:
In [40]: pd.DataFrame(d)
Out[40]:
one two
0 1.0 4.0
1 2.0 3.0
2 3.0 2.0
3 4.0 1.0
In [41]: pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
Out[41]:
one two
a 1.0 4.0
b 2.0 3.0
c 3.0 2.0
d 4.0 1.0
Из структурированного или записывающего массива
Этот случай обрабатывается идентично словарю массивов.
In [42]: data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')])
In [43]: data[:] = [(1,2.,'Hello'), (2,3.,"World")]
In [44]: pd.DataFrame(data)
Out[44]:
A B C
0 1 2.0 Hello
1 2 3.0 World
In [45]: pd.DataFrame(data, index=['first', 'second'])
Out[45]:
A B C
first 1 2.0 Hello
second 2 3.0 World
In [46]: pd.DataFrame(data, columns=['C', 'A', 'B'])
Out[46]:
C A B
0 Hello 1 2.0
1 World 2 3.0
Примечание
DataFrame не предназначен для работы точно так же, как двумерный массив NumPy ndarray.
Из списка словарей
In [47]: data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
In [48]: pd.DataFrame(data2)
Out[48]:
a b c
0 1 2 NaN
1 5 10 20.0
In [49]: pd.DataFrame(data2, index=['first', 'second'])
Out[49]:
a b c
first 1 2 NaN
second 5 10 20.0
In [50]: pd.DataFrame(data2, columns=['a', 'b'])
Out[50]:
a b
0 1 2
1 5 10
Из словаря кортежей
Вы можете автоматически создать таблицу с многоуровневым индексом, передав словарь кортежей
In [51]: pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
....: ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
....: ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
....: ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
....: ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})
....:
Out[51]:
a b
a b c a b
A B 4.0 1.0 5.0 8.0 10.0
C 3.0 2.0 6.0 7.0 NaN
D NaN NaN NaN NaN 9.0
Из ряда
Результат будет DataFrame с тем же индексом, что и входной ряд, и с одним столбцом, имя которого — оригинальное имя ряда (только если другое имя столбца не указано).
Отсутствующие данные
Более подробно об этом будет сказано в разделе про отсутствующие данные. Чтобы создать DataFrame с отсутствующими данными, используйте np.nan для этих значений. В качестве альтернативы, вы можете передать numpy.MaskedArray в качестве аргумента данных конструктору DataFrame, и его замаскированные элементы будут считаться отсутствующими.
Альтернативные конструкторы
DataFrame.from_dict
DataFrame.from_dict принимает словарь словарей или словарь массивоподобных последовательностей и возвращает DataFrame. Он работает как конструктор DataFrame за исключением параметра orient, который по умолчанию является 'columns', но который можно установить на 'index', чтобы использовать ключи словаря в качестве меток строк.
DataFrame.from_records
DataFrame.from_records принимает список кортежей или массив с структурированным типом данных. Работает аналогично обычному конструктору DataFrame, за исключением того, что индекс может быть определённым полем структурированного типа данных для использования в качестве индекса. Например:
In [52]: data
Out[52]:
array([(1, 2.0, 'Hello'), (2, 3.0, 'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
In [53]: pd.DataFrame.from_records(data, index='C')
Out[53]:
A B
C
Hello 1 2.0
World 2 3.0
DataFrame.from_items
DataFrame.from_items работает аналогично форме конструктора dict, который принимает последовательность пар (key, value), где ключи — имена столбцов (или строк в случае orient='index'), а значения — значения столбцов (или строк). Это может быть полезно для построения DataFrame со столбцами в определённом порядке без необходимости передачи явного списка столбцов:
In [54]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])])
Out[54]:
A B
0 1 4
1 2 5
2 3 6
Если вы передадите orient='index', ключи будут метками строк. Но в этом случае вы также должны передать желаемые имена столбцов:
In [55]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
....: orient='index', columns=['one', 'two', 'three'])
....:
Out[55]:
one two three
A 1 2 3
B 4 5 6
Выбор, добавление и удаление столбцов
Вы можете рассматривать DataFrame семантически как словарь объектов Series с одинаковыми индексами. Получение, установка и удаление столбцов работают с той же синтаксической конструкцией, что и аналогичные операции со словарями:
In [56]: df['one'] Out[56]: a 1.0 b 2.0 c 3.0 d NaN Name: one, dtype: float64 In [57]: df['three'] = df['one'] * df['two'] In [58]: df['flag'] = df['one'] > 2 In [59]: df Out[59]: one two three flag a 1.0 1.0 1.0 False b 2.0 2.0 4.0 False c 3.0 3.0 9.0 True d NaN 4.0 NaN False
Столбцы могут быть удалены или удалены с помощью метода pop, как и в словаре:
In [60]: del df['two']
In [61]: three = df.pop('three')
In [62]: df
Out[62]:
one flag
a 1.0 False
b 2.0 False
c 3.0 True
d NaN False
При вставке скалярного значения он естественным образом будет распространён, чтобы заполнить столбец:
In [63]: df['foo'] = 'bar' In [64]: df Out[64]: one flag foo a 1.0 False bar b 2.0 False bar c 3.0 True bar d NaN False bar
При вставке Series, у которой нет такого же индекса, как у DataFrame, она будет приведена к индексу DataFrame:
In [65]: df['one_trunc'] = df['one'][:2] In [66]: df Out[66]: one flag foo one_trunc a 1.0 False bar 1.0 b 2.0 False bar 2.0 c 3.0 True bar NaN d NaN False bar NaN
Вы можете вставлять сырые ndarrays, но их длина должна соответствовать длине индекса DataFrame.
По умолчанию столбцы вставляются в конец. Функция insert доступна для вставки в определённое место в столбцах:
In [67]: df.insert(1, 'bar', df['one']) In [68]: df Out[68]: one bar flag foo one_trunc a 1.0 1.0 False bar 1.0 b 2.0 2.0 False bar 2.0 c 3.0 3.0 True bar NaN d NaN NaN False bar NaN
Назначение новых столбцов в цепочках методов
Новое в версии 0.16.0.
Вдохновлённая глаголом dplyr’s mutate , DataFrame имеет метод assign(), который позволяет легко создавать новые столбцы, потенциально производные от существующих.
In [69]: iris = pd.read_csv('data/iris.data')
In [70]: iris.head()
Out[70]:
SepalLength SepalWidth PetalLength PetalWidth Name
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
In [71]: (iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength'])
....: .head())
....:
Out[71]:
SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio
0 5.1 3.5 1.4 0.2 Iris-setosa 0.6863
1 4.9 3.0 1.4 0.2 Iris-setosa 0.6122
2 4.7 3.2 1.3 0.2 Iris-setosa 0.6809
3 4.6 3.1 1.5 0.2 Iris-setosa 0.6739
4 5.0 3.6 1.4 0.2 Iris-setosa 0.7200
Выше был пример вставки предопределённого значения. Мы также можем передать функцию одного аргумента для её вычисления на DataFrame, которому присваивается значение.
In [72]: iris.assign(sepal_ratio = lambda x: (x['SepalWidth'] / ....: x['SepalLength'])).head() ....: Out[72]: SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio 0 5.1 3.5 1.4 0.2 Iris-setosa 0.6863 1 4.9 3.0 1.4 0.2 Iris-setosa 0.6122 2 4.7 3.2 1.3 0.2 Iris-setosa 0.6809 3 4.6 3.1 1.5 0.2 Iris-setosa 0.6739 4 5.0 3.6 1.4 0.2 Iris-setosa 0.7200
assign **всегда** возвращает копию данных, оставляя исходный DataFrame без изменений.
Передача вызываемого объекта, а не фактического значения для вставки, полезна, когда у вас нет ссылки на DataFrame. Это часто используется при использовании assign в цепочках операций. Например, мы можем ограничить DataFrame только теми наблюдениями, у которых длина Сепалии больше 5, вычислить отношение и построить график:
In [73]: (iris.query('SepalLength > 5')
....: .assign(SepalRatio = lambda x: x.SepalWidth / x.SepalLength,
....: PetalRatio = lambda x: x.PetalWidth / x.PetalLength)
....: .plot(kind='scatter', x='SepalRatio', y='PetalRatio'))
....:
Out[73]: <matplotlib.axes._subplots.AxesSubplot at 0x7ff286891b50>
Поскольку передаётся функция, функция вычисляется на DataFrame, которому присваивается значение. Важно, что это DataFrame, который был отфильтрован до тех строк, у которых длина сепалии больше 5. Сначала происходит фильтрация, а затем вычисления отношения. Это пример, когда у нас не было ссылки на отфильтрованный DataFrame.
Подпись функции assign проста: **kwargs. Ключи — это имена столбцов для новых полей, а значения — это либо значение для вставки (например, Series или массив NumPy), либо функция одного аргумента, вызываемая на DataFrame. Возвращается копия исходного DataFrame с вставленными новыми значениями.
Предупреждение
Поскольку подпись функции assign — это **kwargs, словарь, порядок новых столбцов в результирующем DataFrame не гарантируется, что он будет соответствовать порядку, который вы передаёте. Для предсказуемости элементы вставляются в алфавитном порядке (по ключу) в конец DataFrame.
Все выражения сначала вычисляются, а затем назначаются. Поэтому вы не можете ссылаться на другой столбец, присваиваемый в том же вызове assign. Например:
In [74]: # Don't do this, bad reference to `C`
df.assign(C = lambda x: x['A'] + x['B'],
D = lambda x: x['A'] + x['C'])
In [2]: # Instead, break it into two assigns
(df.assign(C = lambda x: x['A'] + x['B'])
.assign(D = lambda x: x['A'] + x['C']))
Индексирование/Выбор
Основы индексирования таковы:
| Операция | Синтаксис | Результат |
|---|---|---|
| Выбор столбца | df[col] | Series |
| Выбор строки по метке | df.loc[label] | Series |
| Выбор строки по целочисленному расположению | df.iloc[loc] | Series |
| Срез строк | df[5:10] | DataFrame |
| Выбор строк по булевому вектору | df[bool_vec] | DataFrame |
Выбор строки, например, возвращает Series, индекс которой — это столбцы DataFrame:
In [75]: df.loc['b'] Out[75]: one 2 bar 2 flag False foo bar one_trunc 2 Name: b, dtype: object In [76]: df.iloc[2] Out[76]: one 3 bar 3 flag True foo bar one_trunc NaN Name: c, dtype: object
Для более исчерпывающего рассмотрения более сложного индексирования и срезов на основе меток см. раздел по индексированию. Мы рассмотрим основы повторного индексирования/соответствия новым наборам меток в разделе по повторному индексированию.
Выравнивание и арифметика данных
Выравнивание данных между объектами DataFrame автоматически выравнивается по **столбцам и индексу (меткам строк)**. Опять же, результирующий объект будет содержать объединение меток столбцов и строк.
In [77]: df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
In [78]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])
In [79]: df + df2
Out[79]:
A B C D
0 0.5222 0.3225 -0.7566 NaN
1 -0.8441 0.2334 0.8818 NaN
2 -2.2079 -0.1572 -0.3875 NaN
3 2.8080 -1.0927 1.0432 NaN
4 -1.7511 -2.0812 2.7477 NaN
5 -3.2473 -1.0850 0.7898 NaN
6 -1.7107 0.0661 0.1294 NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
9 NaN NaN NaN NaN
При выполнении операции между DataFrame и Series по умолчанию Series выравнивается по **индексу** DataFrame **столбцам**, тем самым векторно расширяя по строкам. Например:
In [80]: df - df.iloc[0]
Out[80]:
A B C D
0 0.0000 0.0000 0.0000 0.0000
1 -2.6396 -1.0702 1.7214 -0.7896
2 -2.7662 -1.6918 2.2776 -2.5401
3 0.8679 -3.5247 1.9365 -0.1331
4 -1.9883 -3.2162 2.0464 -1.0700
5 -3.3932 -4.0976 1.6366 -2.1635
6 -1.3668 -1.9572 1.6523 -0.7191
7 -0.7949 -2.1663 0.9706 -2.6297
8 -0.8383 -1.3630 1.6702 -2.0865
9 0.8588 0.0814 3.7305 -1.3737
В особом случае работы с временными рядами, и индекс DataFrame также содержит даты, расширение будет по столбцам:
In [81]: index = pd.date_range('1/1/2000', periods=8)
In [82]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))
In [83]: df
Out[83]:
A B C
2000-01-01 0.2731 0.3604 -1.1515
2000-01-02 1.1577 1.4787 -0.6528
2000-01-03 -0.7712 0.2203 -0.5739
2000-01-04 -0.6356 -1.1703 -0.0789
2000-01-05 -1.4687 0.1705 -1.8796
2000-01-06 -1.2037 0.9568 -1.1383
2000-01-07 -0.6540 -0.2169 0.3843
2000-01-08 -2.1639 -0.8145 -1.2475
In [84]: type(df['A'])
Out[84]: pandas.core.series.Series
In [85]: df - df['A']
Out[85]:
2000-01-01 00:00:00 2000-01-02 00:00:00 2000-01-03 00:00:00 \
2000-01-01 NaN NaN NaN
2000-01-02 NaN NaN NaN
2000-01-03 NaN NaN NaN
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 NaN NaN NaN
2000-01-04 00:00:00 ... 2000-01-08 00:00:00 A B C
2000-01-01 NaN ... NaN NaN NaN NaN
2000-01-02 NaN ... NaN NaN NaN NaN
2000-01-03 NaN ... NaN NaN NaN NaN
2000-01-04 NaN ... NaN NaN NaN NaN
2000-01-05 NaN ... NaN NaN NaN NaN
2000-01-06 NaN ... NaN NaN NaN NaN
2000-01-07 NaN ... NaN NaN NaN NaN
2000-01-08 NaN ... NaN NaN NaN NaN
[8 rows x 11 columns]
Предупреждение
df - df['A']
теперь устарело и будет удалено в будущей версии. Предпочтительный способ воспроизведения этого поведения:
df.sub(df['A'], axis=0)
Для явного управления поведением сопоставления и расширения см. раздел по гибким бинарным операциям.
Операции со скалярами выполняются так, как и ожидается:
In [86]: df * 5 + 2
Out[86]:
A B C
2000-01-01 3.3655 3.8018 -3.7575
2000-01-02 7.7885 9.3936 -1.2641
2000-01-03 -1.8558 3.1017 -0.8696
2000-01-04 -1.1781 -3.8513 1.6056
2000-01-05 -5.3437 2.8523 -7.3982
2000-01-06 -4.0186 6.7842 -3.6915
2000-01-07 -1.2699 0.9157 3.9217
2000-01-08 -8.8194 -2.0724 -4.2375
In [87]: 1 / df
Out[87]:
A B C
2000-01-01 3.6616 2.7751 -0.8684
2000-01-02 0.8638 0.6763 -1.5318
2000-01-03 -1.2967 4.5383 -1.7424
2000-01-04 -1.5733 -0.8545 -12.6759
2000-01-05 -0.6809 5.8662 -0.5320
2000-01-06 -0.8308 1.0451 -0.8785
2000-01-07 -1.5291 -4.6113 2.6019
2000-01-08 -0.4621 -1.2278 -0.8016
In [88]: df ** 4
Out[88]:
A B C
2000-01-01 0.0056 0.0169 1.7581e+00
2000-01-02 1.7964 4.7813 1.8162e-01
2000-01-03 0.3537 0.0024 1.0849e-01
2000-01-04 0.1632 1.8755 3.8733e-05
2000-01-05 4.6534 0.0008 1.2482e+01
2000-01-06 2.0995 0.8382 1.6789e+00
2000-01-07 0.1829 0.0022 2.1819e-02
2000-01-08 21.9244 0.4401 2.4219e+00
Булевы операторы также работают:
In [89]: df1 = pd.DataFrame({'a' : [1, 0, 1], 'b' : [0, 1, 1] }, dtype=bool)
In [90]: df2 = pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool)
In [91]: df1 & df2
Out[91]:
a b
0 False False
1 False True
2 True False
In [92]: df1 | df2
Out[92]:
a b
0 True True
1 True True
2 True True
In [93]: df1 ^ df2
Out[93]:
a b
0 True True
1 True False
2 False True
In [94]: -df1
Out[94]:
a b
0 False True
1 True False
2 False False
Транспонирование
Для транспонирования используйте атрибут T (также функция transpose), подобно ndarray:
# only show the first 5 rows In [95]: df[:5].T Out[95]: 2000-01-01 2000-01-02 2000-01-03 2000-01-04 2000-01-05 A 0.2731 1.1577 -0.7712 -0.6356 -1.4687 B 0.3604 1.4787 0.2203 -1.1703 0.1705 C -1.1515 -0.6528 -0.5739 -0.0789 -1.8796
Взаимодействие DataFrame с функциями NumPy
Элементные NumPy ufuncs (log, exp, sqrt, ...) и различные другие функции NumPy могут использоваться без проблем с DataFrame, если данные в них являются числовыми:
In [96]: np.exp(df)
Out[96]:
A B C
2000-01-01 1.3140 1.4338 0.3162
2000-01-02 3.1826 4.3873 0.5206
2000-01-03 0.4625 1.2465 0.5633
2000-01-04 0.5296 0.3103 0.9241
2000-01-05 0.2302 1.1859 0.1526
2000-01-06 0.3001 2.6034 0.3204
2000-01-07 0.5200 0.8050 1.4686
2000-01-08 0.1149 0.4429 0.2872
In [97]: np.asarray(df)
Out[97]:
array([[ 0.2731, 0.3604, -1.1515],
[ 1.1577, 1.4787, -0.6528],
[-0.7712, 0.2203, -0.5739],
[-0.6356, -1.1703, -0.0789],
[-1.4687, 0.1705, -1.8796],
[-1.2037, 0.9568, -1.1383],
[-0.654 , -0.2169, 0.3843],
[-2.1639, -0.8145, -1.2475]])
Метод dot DataFrame реализует матричное умножение:
In [98]: df.T.dot(df)
Out[98]:
A B C
A 11.1298 2.8864 6.0015
B 2.8864 5.3895 -1.8913
C 6.0015 -1.8913 8.6204
Аналогично, метод dot Series реализует скалярное произведение:
In [99]: s1 = pd.Series(np.arange(5,10)) In [100]: s1.dot(s1) Out[100]: 255
DataFrame не предназначен для прямого замещения ndarray, так как его семантика индексирования в некоторых местах отличается от матрицы.
Вывод в консоль
Очень большие DataFrame будут усечены, чтобы отобразить их в консоли. Вы также можете получить сводку, используя info(). (Здесь я читаю CSV-версию набора данных baseball из пакета R plyr):
In [101]: baseball = pd.read_csv('data/baseball.csv')
In [102]: print(baseball)
id player year stint ... hbp sh sf gidp
0 88641 womacto01 2006 2 ... 0.0 3.0 0.0 0.0
1 88643 schilcu01 2006 1 ... 0.0 0.0 0.0 0.0
.. ... ... ... ... ... ... ... ... ...
98 89533 aloumo01 2007 1 ... 2.0 0.0 3.0 13.0
99 89534 alomasa02 2007 1 ... 0.0 0.0 0.0 0.0
[100 rows x 23 columns]
In [103]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
id 100 non-null int64
player 100 non-null object
year 100 non-null int64
stint 100 non-null int64
team 100 non-null object
lg 100 non-null object
g 100 non-null int64
ab 100 non-null int64
r 100 non-null int64
h 100 non-null int64
X2b 100 non-null int64
X3b 100 non-null int64
hr 100 non-null int64
rbi 100 non-null float64
sb 100 non-null float64
cs 100 non-null float64
bb 100 non-null int64
so 100 non-null float64
ibb 100 non-null float64
hbp 100 non-null float64
sh 100 non-null float64
sf 100 non-null float64
gidp 100 non-null float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.0+ KB
Однако, использование to_string вернёт строковое представление DataFrame в табличной форме, хотя оно не всегда будет вписываться в ширину консоли:
In [104]: print(baseball.iloc[-20:, :12].to_string())
id player year stint team lg g ab r h X2b X3b
80 89474 finlest01 2007 1 COL NL 43 94 9 17 3 0
81 89480 embreal01 2007 1 OAK AL 4 0 0 0 0 0
82 89481 edmonji01 2007 1 SLN NL 117 365 39 92 15 2
83 89482 easleda01 2007 1 NYN NL 76 193 24 54 6 0
84 89489 delgaca01 2007 1 NYN NL 139 538 71 139 30 0
85 89493 cormirh01 2007 1 CIN NL 6 0 0 0 0 0
86 89494 coninje01 2007 2 NYN NL 21 41 2 8 2 0
87 89495 coninje01 2007 1 CIN NL 80 215 23 57 11 1
88 89497 clemero02 2007 1 NYA AL 2 2 0 1 0 0
89 89498 claytro01 2007 2 BOS AL 8 6 1 0 0 0
90 89499 claytro01 2007 1 TOR AL 69 189 23 48 14 0
91 89501 cirilje01 2007 2 ARI NL 28 40 6 8 4 0
92 89502 cirilje01 2007 1 MIN AL 50 153 18 40 9 2
93 89521 bondsba01 2007 1 SFN NL 126 340 75 94 14 0
94 89523 biggicr01 2007 1 HOU NL 141 517 68 130 31 3
95 89525 benitar01 2007 2 FLO NL 34 0 0 0 0 0
96 89526 benitar01 2007 1 SFN NL 19 0 0 0 0 0
97 89530 ausmubr01 2007 1 HOU NL 117 349 38 82 16 3
98 89533 aloumo01 2007 1 NYN NL 87 328 51 112 19 1
99 89534 alomasa02 2007 1 NYN NL 8 22 1 3 1 0
Начиная с версии 0.10.0, широкие DataFrame по умолчанию будут отображаться по нескольким строкам:
In [105]: pd.DataFrame(np.random.randn(3, 12))
Out[105]:
0 1 2 3 4 5 6 \
0 2.173014 1.273573 0.888325 0.631774 0.206584 -1.745845 -0.505310
1 -1.240418 2.177280 -0.082206 0.827373 -0.700792 0.524540 -1.101396
2 0.269598 -0.453050 -1.821539 -0.126332 -0.153257 0.405483 -0.504557
7 8 9 10 11
0 1.376623 0.741168 -0.509153 -2.012112 -1.204418
1 1.115750 0.294139 0.286939 1.709761 -0.212596
2 1.405148 0.778061 -0.799024 -0.670727 0.086877
Вы можете изменить количество выводимых строк, установив опцию display.width:
In [106]: pd.set_option('display.width', 40) # default is 80
In [107]: pd.DataFrame(np.random.randn(3, 12))
Out[107]:
0 1 2 \
0 1.179465 0.777427 -1.923460
1 0.054928 0.776156 0.372060
2 -0.243404 -1.506557 -1.977226
3 4 5 \
0 0.782432 0.203446 0.250652
1 0.710963 -0.784859 0.168405
2 -0.226582 -0.777971 0.231309
6 7 8 \
0 -2.349580 -0.540814 -0.748939
1 0.159230 0.866492 1.266025
2 1.394479 0.723474 -0.097256
9 10 11
0 -0.994345 1.478624 -0.341991
1 0.555240 0.731803 0.219383
2 0.375274 -0.314401 -2.363136
Вы можете настроить максимальную ширину отдельных столбцов, установив display.max_colwidth
In [108]: datafile={'filename': ['filename_01','filename_02'],
.....: 'path': ["media/user_name/storage/folder_01/filename_01",
.....: "media/user_name/storage/folder_02/filename_02"]}
.....:
In [109]: pd.set_option('display.max_colwidth',30)
In [110]: pd.DataFrame(datafile)
Out[110]:
filename \
0 filename_01
1 filename_02
path
0 media/user_name/storage/fo...
1 media/user_name/storage/fo...
In [111]: pd.set_option('display.max_colwidth',100)
In [112]: pd.DataFrame(datafile)
Out[112]:
filename \
0 filename_01
1 filename_02
path
0 media/user_name/storage/folder_01/filename_01
1 media/user_name/storage/folder_02/filename_02
Вы также можете отключить эту функцию с помощью опции expand_frame_repr. Это отобразит таблицу в одном блоке.
Доступ к атрибутам столбцов DataFrame и завершение ввода IPython
Если метка столбца DataFrame является допустимым именем переменной Python, к столбцу можно обратиться как к атрибутам:
In [113]: df = pd.DataFrame({'foo1' : np.random.randn(5),
.....: 'foo2' : np.random.randn(5)})
.....:
In [114]: df
Out[114]:
foo1 foo2
0 -0.412237 0.213232
1 -0.237644 1.740139
2 1.272869 -0.241491
3 1.220450 -0.868514
4 1.315172 0.407544
In [115]: df.foo1
Out[115]:
0 -0.412237
1 -0.237644
2 1.272869
3 1.220450
4 1.315172
Name: foo1, dtype: float64
Столбцы также связаны с механизмом завершения ввода IPython, поэтому их можно завершать нажатием клавиши Tab:
In [5]: df.fo<TAB> df.foo1 df.foo2
Panel
Panel — это несколько менее используемый, но по-прежнему важный контейнер для трёхмерных данных. Термин panel data происходит из эконометрики и частично ответственен за название pandas: pan(el)-da(ta)-s. Названия трёх осей предназначены для придания некоторого семантического смысла описанию операций, связанных с панельными данными, и, в частности, эконометрического анализа панельных данных. Однако, для строгой цели срезов и нарезки набора объектов DataFrame, вы можете найти имена осей немного произвольными:
- items: ось 0, каждый элемент соответствует DataFrame, содержащемуся внутри
- major_axis: ось 1, это **индекс** (строки) каждого из DataFrames
- minor_axis: ось 2, это **столбцы** каждого из DataFrames
Создание Panel работает примерно так, как вы ожидаете:
Из 3D ndarray с необязательными метками осей
In [116]: 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'])
.....:
In [117]: wp
Out[117]:
<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
Из словаря объектов DataFrame
In [118]: data = {'Item1' : pd.DataFrame(np.random.randn(4, 3)),
.....: 'Item2' : pd.DataFrame(np.random.randn(4, 2))}
.....:
In [119]: pd.Panel(data)
Out[119]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 4 (major_axis) x 3 (minor_axis)
Items axis: Item1 to Item2
Major_axis axis: 0 to 3
Minor_axis axis: 0 to 2
Обратите внимание, что значения в словаре должны быть только **преобразуемыми в DataFrame**. Таким образом, они могут быть любыми другими допустимыми входами в DataFrame, как указано выше.
Одна полезная фабричная функция — Panel.from_dict, которая принимает словарь DataFrame, как выше, и следующие именованные параметры:
| Параметр | Значение по умолчанию | Описание |
|---|---|---|
| intersect | False | удаляет элементы, индексы которых не совпадают |
| orient | items | используйте minor для использования столбцов DataFrame в качестве элементов панели |
Например, сравните с конструкцией выше:
In [120]: pd.Panel.from_dict(data, orient='minor') Out[120]: <class 'pandas.core.panel.Panel'> Dimensions: 3 (items) x 4 (major_axis) x 2 (minor_axis) Items axis: 0 to 2 Major_axis axis: 0 to 3 Minor_axis axis: Item1 to Item2
Orient особенно полезен для DataFrame смешанного типа. Если вы передадите словарь объектов DataFrame со столбцами смешанного типа, все данные будут приведены к типу dtype=object, если вы не передадите orient='minor':
In [121]: df = pd.DataFrame({'a': ['foo', 'bar', 'baz'],
.....: 'b': np.random.randn(3)})
.....:
In [122]: df
Out[122]:
a b
0 foo -1.142863
1 bar -1.015321
2 baz 0.683625
In [123]: data = {'item1': df, 'item2': df}
In [124]: panel = pd.Panel.from_dict(data, orient='minor')
In [125]: panel['a']
Out[125]:
item1 item2
0 foo foo
1 bar bar
2 baz baz
In [126]: panel['b']
Out[126]:
item1 item2
0 -1.142863 -1.142863
1 -1.015321 -1.015321
2 0.683625 0.683625
In [127]: panel['b'].dtypes
Out[127]:
item1 float64
item2 float64
dtype: object
Примечание
К сожалению, Panel, будучи менее используемым, чем Series и DataFrame, был несколько упущен с точки зрения функциональности. Ряд методов и опций, доступных в DataFrame, недоступны в Panel. Конечно, это будет доработано в будущих версиях. И быстрее, если вы присоединитесь ко мне в работе над кодовой базой.
Из DataFrame с помощью метода to_panel
Этот метод был представлен в v0.7 для замены LongPanel.to_long, и преобразует DataFrame с двухъуровневым индексом в Panel.
In [128]: midx = pd.MultiIndex(levels=[['one', 'two'], ['x','y']], labels=[[1,1,0,0],[1,0,1,0]])
In [129]: df = pd.DataFrame({'A' : [1, 2, 3, 4], 'B': [5, 6, 7, 8]}, index=midx)
In [130]: df.to_panel()
Out[130]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 2 (major_axis) x 2 (minor_axis)
Items axis: A to B
Major_axis axis: one to two
Minor_axis axis: x to y
Выбор/добавление/удаление элементов Panel
Аналогично DataFrame, работающему как словарь Series, Panel подобен словарю DataFrame:
In [131]: wp['Item1']
Out[131]:
A B C D
2000-01-01 -0.729430 0.427693 -0.121325 -0.736418
2000-01-02 0.739037 -0.648805 -0.383057 0.385027
2000-01-03 2.321064 -1.290881 0.105458 -1.097035
2000-01-04 0.158759 -1.261191 -0.081710 1.390506
2000-01-05 -1.962031 -0.505580 0.021253 -0.317071
In [132]: wp['Item3'] = wp['Item1'] / wp['Item2']
API для вставки и удаления такой же, как и для DataFrame. И, как и в DataFrame, если элемент является допустимым идентификатором Python, к нему можно получить доступ как к атрибуту и выполнить завершение ввода в IPython.
Транспонирование
Панель может быть переупорядочена с помощью метода transpose (который по умолчанию не создаёт копию, если данные не разнородны):
In [133]: wp.transpose(2, 0, 1) Out[133]: <class 'pandas.core.panel.Panel'> Dimensions: 4 (items) x 3 (major_axis) x 5 (minor_axis) Items axis: A to D Major_axis axis: Item1 to Item3 Minor_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
Индексирование/Выбор
| Операция | Синтаксис | Результат |
|---|---|---|
| Выбрать элемент | wp[item] | DataFrame |
| Получить срез по метке оси major_axis | wp.major_xs(val) | DataFrame |
| Получить срез по метке оси minor_axis | wp.minor_xs(val) | DataFrame |
Например, используя предыдущие данные, мы могли бы сделать:
In [134]: wp['Item1']
Out[134]:
A B C D
2000-01-01 -0.729430 0.427693 -0.121325 -0.736418
2000-01-02 0.739037 -0.648805 -0.383057 0.385027
2000-01-03 2.321064 -1.290881 0.105458 -1.097035
2000-01-04 0.158759 -1.261191 -0.081710 1.390506
2000-01-05 -1.962031 -0.505580 0.021253 -0.317071
In [135]: wp.major_xs(wp.major_axis[2])
Out[135]:
Item1 Item2 Item3
A 2.321064 -0.538606 -4.309389
B -1.290881 0.791512 -1.630905
C 0.105458 -0.020302 -5.194337
D -1.097035 0.184430 -5.948253
In [136]: wp.minor_axis
Out[136]: Index([u'A', u'B', u'C', u'D'], dtype='object')
In [137]: wp.minor_xs('C')
Out[137]:
Item1 Item2 Item3
2000-01-01 -0.121325 1.413524 -0.085832
2000-01-02 -0.383057 1.243178 -0.308127
2000-01-03 0.105458 -0.020302 -5.194337
2000-01-04 -0.081710 -1.811565 0.045105
2000-01-05 0.021253 -1.040542 -0.020425
Сжатие
Другой способ изменить размерность объекта — это squeeze объект с длиной 1, аналогично wp['Item1']
In [138]: wp.reindex(items=['Item1']).squeeze()
Out[138]:
A B C D
2000-01-01 -0.729430 0.427693 -0.121325 -0.736418
2000-01-02 0.739037 -0.648805 -0.383057 0.385027
2000-01-03 2.321064 -1.290881 0.105458 -1.097035
2000-01-04 0.158759 -1.261191 -0.081710 1.390506
2000-01-05 -1.962031 -0.505580 0.021253 -0.317071
In [139]: wp.reindex(items=['Item1'], minor=['B']).squeeze()
Out[139]:
2000-01-01 0.427693
2000-01-02 -0.648805
2000-01-03 -1.290881
2000-01-04 -1.261191
2000-01-05 -0.505580
Freq: D, Name: B, dtype: float64
Преобразование в DataFrame
Панель может быть представлена в 2D-форме как DataFrame с иерархическим индексом. Подробности см. в разделе иерархическое индексирование. Для преобразования панели в DataFrame используйте метод to_frame:
In [140]: panel = pd.Panel(np.random.randn(3, 5, 4), items=['one', 'two', 'three'],
.....: major_axis=pd.date_range('1/1/2000', periods=5),
.....: minor_axis=['a', 'b', 'c', 'd'])
.....:
In [141]: panel.to_frame()
Out[141]:
one two three
major minor
2000-01-01 a -1.876826 -0.383171 -0.117339
b -1.873827 -0.172217 0.780048
c -0.251457 -1.674685 2.162047
d 0.027599 0.762474 0.874233
2000-01-02 a 1.235291 0.481666 -0.764147
b 0.850574 1.217546 -0.484495
c -1.140302 0.577103 0.298570
d 2.149143 -0.076021 0.825136
2000-01-03 a 0.504452 0.720235 -0.388020
b 0.678026 0.202660 -0.339279
c -0.628443 -0.314950 0.141164
d 1.191156 -0.410852 0.565930
2000-01-04 a -1.145363 0.542758 -1.749969
b -0.523153 1.955407 -1.402941
c -1.299878 -0.940645 0.623222
d -0.110240 0.076257 0.020129
2000-01-05 a -0.333712 -0.897159 -2.858463
b 0.416876 -1.265679 0.885765
c -0.436400 -0.528311 0.158014
d 0.999768 -0.660014 -1.981797
Panel4D и PanelND (Устаревшие)
Предупреждение
В версии 0.19.0 Panel4D и PanelND устарели и будут удалены в будущих версиях. Рекомендуемый способ представления данных с n-мерными данными — использование пакета xarray. Pandas предоставляет метод to_xarray() для автоматизации этого преобразования.
См. документацию предыдущей версии для документации по этим объектам.
© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.19.2/dsintro.html