Spec-Zone.ru › pandas 0.19

Введение в структуры данных

Мы начнём с быстрого, но неполного обзора основных структур данных в 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>
_images/basics_assign.png

Поскольку передаётся функция, функция вычисляется на 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

Spec-Zone.ru

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