Преобразование формы и сводные таблицы
Преобразование формы путем поворота объектов DataFrame
Данные часто хранятся в файлах CSV или базах данных в так называемом «упакованном» или «записновом» формате:
In [1]: df
Out[1]:
date variable value
0 2000-01-03 A 0.469112
1 2000-01-04 A -0.282863
2 2000-01-05 A -1.509059
3 2000-01-03 B -1.135632
4 2000-01-04 B 1.212112
5 2000-01-05 B -0.173215
6 2000-01-03 C 0.119209
7 2000-01-04 C -1.044236
8 2000-01-05 C -0.861849
9 2000-01-03 D -2.104569
10 2000-01-04 D -0.494929
11 2000-01-05 D 1.071804
Для тех, кто интересуется, вот как был создан приведенный выше DataFrame:
import pandas.util.testing as tm; tm.N = 3
def unpivot(frame):
N, K = frame.shape
data = {'value' : frame.values.ravel('F'),
'variable' : np.asarray(frame.columns).repeat(N),
'date' : np.tile(np.asarray(frame.index), K)}
return pd.DataFrame(data, columns=['date', 'variable', 'value'])
df = unpivot(tm.makeTimeDataFrame())
Чтобы выбрать все для переменной A, мы можем сделать следующее:
In [2]: df[df['variable'] == 'A']
Out[2]:
date variable value
0 2000-01-03 A 0.469112
1 2000-01-04 A -0.282863
2 2000-01-05 A -1.509059
Но предположим, что мы хотим выполнить операции с временными рядами с переменными. Лучшим представлением было бы то, где columns — это уникальные переменные, а index дат идентифицирует отдельные наблюдения. Чтобы преобразовать данные в этот формат, используйте функцию pivot:
In [3]: df.pivot(index='date', columns='variable', values='value') Out[3]: variable A B C D date 2000-01-03 0.469112 -1.135632 0.119209 -2.104569 2000-01-04 -0.282863 1.212112 -1.044236 -0.494929 2000-01-05 -1.509059 -0.173215 -0.861849 1.071804
Если аргумент values опущен, и входной DataFrame содержит более одной колонки значений, которые не используются в качестве колонок или индексов для ввода в pivot, то результирующий «повернутый» DataFrame будет иметь многоуровневые столбцы, верхний уровень которых указывает соответствующую колонку значений:
In [4]: df['value2'] = df['value'] * 2
In [5]: pivoted = df.pivot('date', 'variable')
In [6]: pivoted
Out[6]:
value value2 \
variable A B C D A B
date
2000-01-03 0.469112 -1.135632 0.119209 -2.104569 0.938225 -2.271265
2000-01-04 -0.282863 1.212112 -1.044236 -0.494929 -0.565727 2.424224
2000-01-05 -1.509059 -0.173215 -0.861849 1.071804 -3.018117 -0.346429
variable C D
date
2000-01-03 0.238417 -4.209138
2000-01-04 -2.088472 -0.989859
2000-01-05 -1.723698 2.143608
Конечно, вы можете затем выбрать подмножества из повернутого DataFrame:
In [7]: pivoted['value2'] Out[7]: variable A B C D date 2000-01-03 0.938225 -2.271265 0.238417 -4.209138 2000-01-04 -0.565727 2.424224 -2.088472 -0.989859 2000-01-05 -3.018117 -0.346429 -1.723698 2.143608
Обратите внимание, что это возвращает представление данных в базовом наборе данных в случае, если данные имеют однородный тип.
Преобразование формы путем склеивания и разворачивания
Тесно связанные с функцией pivot функции stack и unstack в настоящее время доступны для Series и DataFrame. Эти функции предназначены для совместной работы с объектами MultiIndex (см. раздел о многоуровневом индексировании). Вот, по существу, что делают эти функции:
-
stack: «повернуть» уровень меток столбцов (возможно, многоуровневых), возвращая DataFrame с индексом с новым внутренним уровнем меток строк. -
unstack: обратная операция отstack: «повернуть» уровень (возможно, многоуровневого) индекса строк к оси столбцов, создавая преобразованный DataFrame с новым внутренним уровнем меток столбцов.
Самый ясный способ объяснения — на примере. Возьмем предыдущий пример набора данных из раздела многоуровневого индексирования:
In [8]: tuples = list(zip(*[['bar', 'bar', 'baz', 'baz',
...: 'foo', 'foo', 'qux', 'qux'],
...: ['one', 'two', 'one', 'two',
...: 'one', 'two', 'one', 'two']]))
...:
In [9]: index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
In [10]: df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])
In [11]: df2 = df[:4]
In [12]: df2
Out[12]:
A B
first second
bar one 0.721555 -0.706771
two -1.039575 0.271860
baz one -0.424972 0.567020
two 0.276232 -1.087401
Функция stack «сжимает» уровень столбцов DataFrame, чтобы создать:
- Series в случае простого индекса столбцов
- DataFrame в случае
MultiIndexв столбцах
Если у столбцов есть MultiIndex, вы можете выбрать, какой уровень нужно склеить. Склеенный уровень станет новым самым нижним уровнем в MultiIndex по столбцам:
In [13]: stacked = df2.stack()
In [14]: stacked
Out[14]:
first second
bar one A 0.721555
B -0.706771
two A -1.039575
B 0.271860
baz one A -0.424972
B 0.567020
two A 0.276232
B -1.087401
dtype: float64
В случае «склеенного» DataFrame или Series (имеющего MultiIndex в качестве index ), обратной операцией к stack является unstack, которая по умолчанию разворачивает последний уровень:
In [15]: stacked.unstack()
Out[15]:
A B
first second
bar one 0.721555 -0.706771
two -1.039575 0.271860
baz one -0.424972 0.567020
two 0.276232 -1.087401
In [16]: stacked.unstack(1)
Out[16]:
second one two
first
bar A 0.721555 -1.039575
B -0.706771 0.271860
baz A -0.424972 0.276232
B 0.567020 -1.087401
In [17]: stacked.unstack(0)
Out[17]:
first bar baz
second
one A 0.721555 -0.424972
B -0.706771 0.567020
two A -1.039575 0.276232
B 0.271860 -1.087401
Если у индексов есть имена, вы можете использовать имена уровней вместо указания номеров уровней:
In [18]: stacked.unstack('second')
Out[18]:
second one two
first
bar A 0.721555 -1.039575
B -0.706771 0.271860
baz A -0.424972 0.276232
B 0.567020 -1.087401
Обратите внимание, что методы stack и unstack неявно сортируют уровни индексов. Поэтому вызов stack и затем unstack, или наоборот, приведет к отсортированной копии исходного DataFrame или Series:
In [19]: index = pd.MultiIndex.from_product([[2,1], ['a', 'b']])
In [20]: df = pd.DataFrame(np.random.randn(4), index=index, columns=['A'])
In [21]: df
Out[21]:
A
2 a -0.370647
b -1.157892
1 a -1.344312
b 0.844885
In [22]: all(df.unstack().stack() == df.sort_index())
Out[22]: True
в то время как приведенный выше код вызовет TypeError, если вызов sort_index будет удален.
Несколько уровней
Вы также можете склеить или развернуть более одного уровня за раз, передав список уровней, в этом случае конечный результат будет таким, как если бы каждый уровень в списке обрабатывался индивидуально.
In [23]: columns = pd.MultiIndex.from_tuples([
....: ('A', 'cat', 'long'), ('B', 'cat', 'long'),
....: ('A', 'dog', 'short'), ('B', 'dog', 'short')
....: ],
....: names=['exp', 'animal', 'hair_length']
....: )
....:
In [24]: df = pd.DataFrame(np.random.randn(4, 4), columns=columns)
In [25]: df
Out[25]:
exp A B A B
animal cat cat dog dog
hair_length long long short short
0 1.075770 -0.109050 1.643563 -1.469388
1 0.357021 -0.674600 -1.776904 -0.968914
2 -1.294524 0.413738 0.276662 -0.472035
3 -0.013960 -0.362543 -0.006154 -0.923061
In [26]: df.stack(level=['animal', 'hair_length'])
Out[26]:
exp A B
animal hair_length
0 cat long 1.075770 -0.109050
dog short 1.643563 -1.469388
1 cat long 0.357021 -0.674600
dog short -1.776904 -0.968914
2 cat long -1.294524 0.413738
dog short 0.276662 -0.472035
3 cat long -0.013960 -0.362543
dog short -0.006154 -0.923061
Список уровней может содержать либо имена уровней, либо номера уровней (но не их смесь).
# df.stack(level=['animal', 'hair_length']) # from above is equivalent to: In [27]: df.stack(level=[1, 2]) Out[27]: exp A B animal hair_length 0 cat long 1.075770 -0.109050 dog short 1.643563 -1.469388 1 cat long 0.357021 -0.674600 dog short -1.776904 -0.968914 2 cat long -1.294524 0.413738 dog short 0.276662 -0.472035 3 cat long -0.013960 -0.362543 dog short -0.006154 -0.923061
Пропущенные данные
Эти функции грамотно обрабатывают пропущенные данные и не ожидают, что каждая подгруппа в иерархическом индексе будет иметь тот же набор меток. Они также могут обрабатывать несортированный индекс (но, конечно, вы можете сделать его отсортированным, вызвав sort_index). Вот более сложный пример:
In [28]: columns = pd.MultiIndex.from_tuples([('A', 'cat'), ('B', 'dog'),
....: ('B', 'cat'), ('A', 'dog')],
....: names=['exp', 'animal'])
....:
In [29]: index = pd.MultiIndex.from_product([('bar', 'baz', 'foo', 'qux'),
....: ('one', 'two')],
....: names=['first', 'second'])
....:
In [30]: df = pd.DataFrame(np.random.randn(8, 4), index=index, columns=columns)
In [31]: df2 = df.ix[[0, 1, 2, 4, 5, 7]]
In [32]: df2
Out[32]:
exp A B A
animal cat dog cat dog
first second
bar one 0.895717 0.805244 -1.206412 2.565646
two 1.431256 1.340309 -1.170299 -0.226169
baz one 0.410835 0.813850 0.132003 -0.827317
foo one -1.413681 1.607920 1.024180 0.569605
two 0.875906 -2.211372 0.974466 -2.006747
qux two -1.226825 0.769804 -1.281247 -0.727707
Как упоминалось выше, stack может быть вызван с аргументом level для выбора уровня в столбцах, который нужно склеить:
In [33]: df2.stack('exp')
Out[33]:
animal cat dog
first second exp
bar one A 0.895717 2.565646
B -1.206412 0.805244
two A 1.431256 -0.226169
B -1.170299 1.340309
baz one A 0.410835 -0.827317
B 0.132003 0.813850
foo one A -1.413681 0.569605
B 1.024180 1.607920
two A 0.875906 -2.006747
B 0.974466 -2.211372
qux two A -1.226825 -0.727707
B -1.281247 0.769804
In [34]: df2.stack('animal')
Out[34]:
exp A B
first second animal
bar one cat 0.895717 -1.206412
dog 2.565646 0.805244
two cat 1.431256 -1.170299
dog -0.226169 1.340309
baz one cat 0.410835 0.132003
dog -0.827317 0.813850
foo one cat -1.413681 1.024180
dog 0.569605 1.607920
two cat 0.875906 0.974466
dog -2.006747 -2.211372
qux two cat -1.226825 -1.281247
dog -0.727707 0.769804
Разворачивание может привести к пропущенным значениям, если подгруппы не имеют одинакового набора меток. По умолчанию пропущенные значения будут заменены значением заполнения по умолчанию для этого типа данных, NaN для float, NaT для datetimelike и т. д. Для целочисленных типов по умолчанию данные будут преобразованы в float, а пропущенные значения будут установлены в NaN.
In [35]: df3 = df.iloc[[0, 1, 4, 7], [1, 2]]
In [36]: df3
Out[36]:
exp B
animal dog cat
first second
bar one 0.805244 -1.206412
two 1.340309 -1.170299
foo one 1.607920 1.024180
qux two 0.769804 -1.281247
In [37]: df3.unstack()
Out[37]:
exp B
animal dog cat
second one two one two
first
bar 0.805244 1.340309 -1.206412 -1.170299
foo 1.607920 NaN 1.024180 NaN
qux NaN 0.769804 NaN -1.281247
В качестве альтернативы, разворачивание принимает необязательный аргумент fill_value для указания значения пропущенных данных.
In [38]: df3.unstack(fill_value=-1e9) Out[38]: exp B animal dog cat second one two one two first bar 8.052440e-01 1.340309e+00 -1.206412e+00 -1.170299e+00 foo 1.607920e+00 -1.000000e+09 1.024180e+00 -1.000000e+09 qux -1.000000e+09 7.698036e-01 -1.000000e+09 -1.281247e+00
С многоуровневым индексом
Разворачивание, когда столбцы представляют собой MultiIndex , также тщательно следит за тем, чтобы сделать всё правильно:
In [39]: df[:3].unstack(0) Out[39]: exp A B A \ animal cat dog cat dog first bar baz bar baz bar baz bar second one 0.895717 0.410835 0.805244 0.81385 -1.206412 0.132003 2.565646 two 1.431256 NaN 1.340309 NaN -1.170299 NaN -0.226169 exp animal first baz second one -0.827317 two NaN In [40]: df2.unstack(1) Out[40]: exp A B A \ animal cat dog cat dog second one two one two one two one first bar 0.895717 1.431256 0.805244 1.340309 -1.206412 -1.170299 2.565646 baz 0.410835 NaN 0.813850 NaN 0.132003 NaN -0.827317 foo -1.413681 0.875906 1.607920 -2.211372 1.024180 0.974466 0.569605 qux NaN -1.226825 NaN 0.769804 NaN -1.281247 NaN exp animal second two first bar -0.226169 baz NaN foo -2.006747 qux -0.727707
Преобразование формы с помощью Melt
Функция melt() полезна для преобразования DataFrame в формат, где одна или несколько колонок являются идентификационными переменными, а все остальные колонки, рассматриваемые как измеряемые переменные, «разворачиваются» до оси строк, оставляя только две колонки, не являющиеся идентификаторами, «переменная» и «значение». Названия этих колонок можно настроить, используя параметры var_name и value_name.
Например,
In [41]: cheese = pd.DataFrame({'first' : ['John', 'Mary'],
....: 'last' : ['Doe', 'Bo'],
....: 'height' : [5.5, 6.0],
....: 'weight' : [130, 150]})
....:
In [42]: cheese
Out[42]:
first height last weight
0 John 5.5 Doe 130
1 Mary 6.0 Bo 150
In [43]: pd.melt(cheese, id_vars=['first', 'last'])
Out[43]:
first last variable value
0 John Doe height 5.5
1 Mary Bo height 6.0
2 John Doe weight 130.0
3 Mary Bo weight 150.0
In [44]: pd.melt(cheese, id_vars=['first', 'last'], var_name='quantity')
Out[44]:
first last quantity value
0 John Doe height 5.5
1 Mary Bo height 6.0
2 John Doe weight 130.0
3 Mary Bo weight 150.0
Другой способ преобразования — использовать функцию удобства панельных данных wide_to_long.
In [45]: dft = pd.DataFrame({"A1970" : {0 : "a", 1 : "b", 2 : "c"},
....: "A1980" : {0 : "d", 1 : "e", 2 : "f"},
....: "B1970" : {0 : 2.5, 1 : 1.2, 2 : .7},
....: "B1980" : {0 : 3.2, 1 : 1.3, 2 : .1},
....: "X" : dict(zip(range(3), np.random.randn(3)))
....: })
....:
In [46]: dft["id"] = dft.index
In [47]: dft
Out[47]:
A1970 A1980 B1970 B1980 X id
0 a d 2.5 3.2 -0.121306 0
1 b e 1.2 1.3 -0.097883 1
2 c f 0.7 0.1 0.695775 2
In [48]: pd.wide_to_long(dft, ["A", "B"], i="id", j="year")
Out[48]:
X A B
id year
0 1970 -0.121306 a 2.5
1 1970 -0.097883 b 1.2
2 1970 0.695775 c 0.7
0 1980 -0.121306 d 3.2
1 1980 -0.097883 e 1.3
2 1980 0.695775 f 0.1
Комбинирование со статистикой и GroupBy
Неудивительно, что объединение pivot / stack / unstack с GroupBy и базовыми статистическими функциями Series и DataFrame может привести к некоторым очень выразительным и быстрым манипуляциям с данными.
In [49]: df
Out[49]:
exp A B A
animal cat dog cat dog
first second
bar one 0.895717 0.805244 -1.206412 2.565646
two 1.431256 1.340309 -1.170299 -0.226169
baz one 0.410835 0.813850 0.132003 -0.827317
two -0.076467 -1.187678 1.130127 -1.436737
foo one -1.413681 1.607920 1.024180 0.569605
two 0.875906 -2.211372 0.974466 -2.006747
qux one -0.410001 -0.078638 0.545952 -1.219217
two -1.226825 0.769804 -1.281247 -0.727707
In [50]: df.stack().mean(1).unstack()
Out[50]:
animal cat dog
first second
bar one -0.155347 1.685445
two 0.130479 0.557070
baz one 0.271419 -0.006733
two 0.526830 -1.312207
foo one -0.194750 1.088763
two 0.925186 -2.109060
qux one 0.067976 -0.648927
two -1.254036 0.021048
# same result, another way
In [51]: df.groupby(level=1, axis=1).mean()
Out[51]:
animal cat dog
first second
bar one -0.155347 1.685445
two 0.130479 0.557070
baz one 0.271419 -0.006733
two 0.526830 -1.312207
foo one -0.194750 1.088763
two 0.925186 -2.109060
qux one 0.067976 -0.648927
two -1.254036 0.021048
In [52]: df.stack().groupby(level=1).mean()
Out[52]:
exp A B
second
one 0.071448 0.455513
two -0.424186 -0.204486
In [53]: df.mean().unstack(0)
Out[53]:
exp A B
animal
cat 0.060843 0.018596
dog -0.413580 0.232430
Сводные таблицы
Функция pandas.pivot_table может использоваться для создания сводных таблиц в стиле электронных таблиц. См. кулинарную книгу для некоторых продвинутых стратегий
Она принимает ряд аргументов
-
data: объект DataFrame -
values: колонка или список колонок для агрегирования -
index: колонка, Grouper, массив, имеющий ту же длину, что и данные, или список из них. Ключи для группировки по индексу сводной таблицы. Если передан массив, он используется аналогичным образом, как значения столбцов. -
columns: колонка, Grouper, массив, имеющий ту же длину, что и данные, или список из них. Ключи для группировки по столбцу сводной таблицы. Если передан массив, он используется аналогичным образом, как значения столбцов. -
aggfunc: функция для агрегирования, по умолчаниюnumpy.mean
Рассмотрим набор данных, похожий на этот:
In [54]: import datetime
In [55]: df = pd.DataFrame({'A': ['one', 'one', 'two', 'three'] * 6,
....: 'B': ['A', 'B', 'C'] * 8,
....: 'C': ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 4,
....: 'D': np.random.randn(24),
....: 'E': np.random.randn(24),
....: 'F': [datetime.datetime(2013, i, 1) for i in range(1, 13)] +
....: [datetime.datetime(2013, i, 15) for i in range(1, 13)]})
....:
In [56]: df
Out[56]:
A B C D E F
0 one A foo 0.341734 -0.317441 2013-01-01
1 one B foo 0.959726 -1.236269 2013-02-01
2 two C foo -1.110336 0.896171 2013-03-01
3 three A bar -0.619976 -0.487602 2013-04-01
4 one B bar 0.149748 -0.082240 2013-05-01
5 one C bar -0.732339 -2.182937 2013-06-01
6 two A foo 0.687738 0.380396 2013-07-01
.. ... .. ... ... ... ...
17 one C bar -0.345352 0.206053 2013-06-15
18 two A foo 1.314232 -0.251905 2013-07-15
19 three B foo 0.690579 -2.213588 2013-08-15
20 one C foo 0.995761 1.063327 2013-09-15
21 one A bar 2.396780 1.266143 2013-10-15
22 two B bar 0.014871 0.299368 2013-11-15
23 three C bar 3.357427 -0.863838 2013-12-15
[24 rows x 6 columns]
Мы можем очень легко создать сводные таблицы из этих данных:
In [57]: pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'])
Out[57]:
C bar foo
A B
one A 1.120915 -0.514058
B -0.338421 0.002759
C -0.538846 0.699535
three A -1.181568 NaN
B NaN 0.433512
C 0.588783 NaN
two A NaN 1.000985
B 0.158248 NaN
C NaN 0.176180
In [58]: pd.pivot_table(df, values='D', index=['B'], columns=['A', 'C'], aggfunc=np.sum)
Out[58]:
A one three two
C bar foo bar foo bar foo
B
A 2.241830 -1.028115 -2.363137 NaN NaN 2.001971
B -0.676843 0.005518 NaN 0.867024 0.316495 NaN
C -1.077692 1.399070 1.177566 NaN NaN 0.352360
In [59]: pd.pivot_table(df, values=['D','E'], index=['B'], columns=['A', 'C'], aggfunc=np.sum)
Out[59]:
D E \
A one three two one
C bar foo bar foo bar foo bar
B
A 2.241830 -1.028115 -2.363137 NaN NaN 2.001971 2.786113
B -0.676843 0.005518 NaN 0.867024 0.316495 NaN 1.368280
C -1.077692 1.399070 1.177566 NaN NaN 0.352360 -1.976883
A three two
C foo bar foo bar foo
B
A -0.043211 1.922577 NaN NaN 0.128491
B -1.103384 NaN -2.128743 -0.194294 NaN
C 1.495717 -0.263660 NaN NaN 0.872482
Результат — DataFrame с потенциально многоуровневыми индексами в строках и столбцах. Если имя колонки values не указано, сводная таблица будет включать все данные, которые можно агрегировать, в дополнительный уровень иерархии в столбцах:
In [60]: pd.pivot_table(df, index=['A', 'B'], columns=['C'])
Out[60]:
D E
C bar foo bar foo
A B
one A 1.120915 -0.514058 1.393057 -0.021605
B -0.338421 0.002759 0.684140 -0.551692
C -0.538846 0.699535 -0.988442 0.747859
three A -1.181568 NaN 0.961289 NaN
B NaN 0.433512 NaN -1.064372
C 0.588783 NaN -0.131830 NaN
two A NaN 1.000985 NaN 0.064245
B 0.158248 NaN -0.097147 NaN
C NaN 0.176180 NaN 0.436241
Кроме того, вы можете использовать Grouper для ключевых слов index и columns. Подробности о Grouper, см. Группирование с указанием Grouper.
In [61]: pd.pivot_table(df, values='D', index=pd.Grouper(freq='M', key='F'), columns='C') Out[61]: C bar foo F 2013-01-31 NaN -0.514058 2013-02-28 NaN 0.002759 2013-03-31 NaN 0.176180 2013-04-30 -1.181568 NaN 2013-05-31 -0.338421 NaN 2013-06-30 -0.538846 NaN 2013-07-31 NaN 1.000985 2013-08-31 NaN 0.433512 2013-09-30 NaN 0.699535 2013-10-31 1.120915 NaN 2013-11-30 0.158248 NaN 2013-12-31 0.588783 NaN
Вы можете отобразить красивую выходную таблицу, опуская пропущенные значения, вызвав to_string если хотите:
In [62]: table = pd.pivot_table(df, index=['A', 'B'], columns=['C'])
In [63]: print(table.to_string(na_rep=''))
D E
C bar foo bar foo
A B
one A 1.120915 -0.514058 1.393057 -0.021605
B -0.338421 0.002759 0.684140 -0.551692
C -0.538846 0.699535 -0.988442 0.747859
three A -1.181568 0.961289
B 0.433512 -1.064372
C 0.588783 -0.131830
two A 1.000985 0.064245
B 0.158248 -0.097147
C 0.176180 0.436241
Обратите внимание, что pivot_table также доступен в качестве метода экземпляра DataFrame.
Добавление полей «margin»
Если вы передадите margins=True в pivot_table, будут добавлены специальные столбцы и строки All с агрегатами частичных групп по категориям в строках и столбцах:
In [64]: df.pivot_table(index=['A', 'B'], columns='C', margins=True, aggfunc=np.std)
Out[64]:
D E
C bar foo All bar foo All
A B
one A 1.804346 1.210272 1.569879 0.179483 0.418374 0.858005
B 0.690376 1.353355 0.898998 1.083825 0.968138 1.101401
C 0.273641 0.418926 0.771139 1.689271 0.446140 1.422136
three A 0.794212 NaN 0.794212 2.049040 NaN 2.049040
B NaN 0.363548 0.363548 NaN 1.625237 1.625237
C 3.915454 NaN 3.915454 1.035215 NaN 1.035215
two A NaN 0.442998 0.442998 NaN 0.447104 0.447104
B 0.202765 NaN 0.202765 0.560757 NaN 0.560757
C NaN 1.819408 1.819408 NaN 0.650439 0.650439
All 1.556686 0.952552 1.246608 1.250924 0.899904 1.059389
Перекрестные сводные таблицы
Используйте функцию crosstab для вычисления перекрестной сводной таблицы двух (или более) факторов. По умолчанию crosstab вычисляет таблицу частот факторов, если не передан массив значений и функция агрегирования.
Она принимает ряд аргументов
-
index: массив-подобный объект, значения для группировки в строках -
columns: массив-подобный объект, значения для группировки в столбцах -
values: массив-подобный объект, необязательно, массив значений для агрегирования в соответствии с факторами -
aggfunc: функция, необязательно, Если массив значений не передан, вычисляется таблица частот -
rownames: последовательность, по умолчаниюNone, должна соответствовать количеству переданных массивов строк -
colnames: последовательность, по умолчаниюNone, если передана, должна соответствовать количеству переданных массивов столбцов -
margins: булево, по умолчаниюFalse, добавить поля «margin» (подсчёты по строкам/столбцам) -
normalize: булево, {‘all’, ‘index’, ‘columns’}, или {0,1}, по умолчаниюFalse. Нормализовать, разделив все значения на сумму значений.
Любая переданная Series будет использовать свои атрибуты имени, если не указаны имена строк или столбцов для перекрестной сводной таблицы
Например:
In [65]: foo, bar, dull, shiny, one, two = 'foo', 'bar', 'dull', 'shiny', 'one', 'two' In [66]: a = np.array([foo, foo, bar, bar, foo, foo], dtype=object) In [67]: b = np.array([one, one, two, one, two, one], dtype=object) In [68]: c = np.array([dull, dull, shiny, dull, dull, shiny], dtype=object) In [69]: pd.crosstab(a, [b, c], rownames=['a'], colnames=['b', 'c']) Out[69]: b one two c dull shiny dull shiny a bar 1 0 0 1 foo 2 1 1 0
Если crosstab получает только две Series, она предоставит таблицу частот.
In [70]: df = pd.DataFrame({'A': [1, 2, 2, 2, 2], 'B': [3, 3, 4, 4, 4],
....: 'C': [1, 1, np.nan, 1, 1]})
....:
In [71]: df
Out[71]:
A B C
0 1 3 1.0
1 2 3 1.0
2 2 4 NaN
3 2 4 1.0
4 2 4 1.0
In [72]: pd.crosstab(df.A, df.B)
Out[72]:
B 3 4
A
1 1 0
2 1 3
Любой переданный ввод, содержащий Categorical данные, будет включать все его категории в перекрестной сводной таблице, даже если фактические данные не содержат никаких экземпляров определённой категории.
In [73]: foo = pd.Categorical(['a', 'b'], categories=['a', 'b', 'c']) In [74]: bar = pd.Categorical(['d', 'e'], categories=['d', 'e', 'f']) In [75]: pd.crosstab(foo, bar) Out[75]: col_0 d e f row_0 a 1 0 0 b 0 1 0 c 0 0 0
Нормализация
Добавлено в версии 0.18.1.
Таблицы частот также могут быть нормализованы, чтобы отображать проценты, а не подсчеты, с использованием аргумента normalize:
In [76]: pd.crosstab(df.A, df.B, normalize=True) Out[76]: B 3 4 A 1 0.2 0.0 2 0.2 0.6
normalize также может нормализовать значения в каждой строке или в каждом столбце:
In [77]: pd.crosstab(df.A, df.B, normalize='columns') Out[77]: B 3 4 A 1 0.5 0.0 2 0.5 1.0
crosstab также может принимать третью Series и функцию агрегирования (aggfunc) , которая будет применяться к значениям третьей Series внутри каждой группы, определенной первыми двумя Series:
In [78]: pd.crosstab(df.A, df.B, values=df.C, aggfunc=np.sum) Out[78]: B 3 4 A 1 1.0 NaN 2 1.0 2.0
Добавление полей «margin»
Наконец, можно также добавить отступы или нормализовать этот вывод.
In [79]: pd.crosstab(df.A, df.B, values=df.C, aggfunc=np.sum, normalize=True, ....: margins=True) ....: Out[79]: B 3 4 All A 1 0.25 0.0 0.25 2 0.25 0.5 0.75 All 0.50 0.5 1.00
Разбиение на группы
Функция cut вычисляет группировки для значений входного массива и часто используется для преобразования непрерывных переменных в дискретные или категориальные:
In [80]: ages = np.array([10, 15, 13, 12, 23, 25, 28, 59, 60]) In [81]: pd.cut(ages, bins=3) Out[81]: [(9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (26.667, 43.333], (43.333, 60], (43.333, 60]] Categories (3, object): [(9.95, 26.667] < (26.667, 43.333] < (43.333, 60]]
Если ключевое слово bins является целым числом, то формируются равные по ширине интервалы. В качестве альтернативы, мы можем указать пользовательские границы интервалов:
In [82]: pd.cut(ages, bins=[0, 18, 35, 70]) Out[82]: [(0, 18], (0, 18], (0, 18], (0, 18], (18, 35], (18, 35], (18, 35], (35, 70], (35, 70]] Categories (3, object): [(0, 18] < (18, 35] < (35, 70]]
Вычисление индикаторных/фиктивных переменных
Для преобразования категориальной переменной в «фиктивную» или «индикаторную» таблицу DataFrame, например, столбца в DataFrame (Series), имеющего k различных значений, можно получить DataFrame, содержащий k столбцов из 1 и 0:
In [83]: df = pd.DataFrame({'key': list('bbacab'), 'data1': range(6)})
In [84]: pd.get_dummies(df['key'])
Out[84]:
a b c
0 0 1 0
1 0 1 0
2 1 0 0
3 0 0 1
4 1 0 0
5 0 1 0
Иногда полезно добавлять префиксы к именам столбцов, например, при объединении результата с исходным DataFrame:
In [85]: dummies = pd.get_dummies(df['key'], prefix='key') In [86]: dummies Out[86]: key_a key_b key_c 0 0 1 0 1 0 1 0 2 1 0 0 3 0 0 1 4 1 0 0 5 0 1 0 In [87]: df[['data1']].join(dummies) Out[87]: data1 key_a key_b key_c 0 0 0 1 0 1 1 0 1 0 2 2 1 0 0 3 3 0 0 1 4 4 1 0 0 5 5 0 1 0
Эта функция часто используется вместе с функциями дискретизации, такими как cut:
In [88]: values = np.random.randn(10)
In [89]: values
Out[89]:
array([ 0.4082, -1.0481, -0.0257, -0.9884, 0.0941, 1.2627, 1.29 ,
0.0824, -0.0558, 0.5366])
In [90]: bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
In [91]: pd.get_dummies(pd.cut(values, bins))
Out[91]:
(0, 0.2] (0.2, 0.4] (0.4, 0.6] (0.6, 0.8] (0.8, 1]
0 0 0 1 0 0
1 0 0 0 0 0
2 0 0 0 0 0
3 0 0 0 0 0
4 1 0 0 0 0
5 0 0 0 0 0
6 0 0 0 0 0
7 1 0 0 0 0
8 0 0 0 0 0
9 0 0 1 0 0
См. также Series.str.get_dummies.
Новая в версии 0.15.0.
get_dummies() также принимает DataFrame. По умолчанию все категориальные переменные (категориальные в статистическом смысле, те, у которых object или categorical тип данных) кодируются как фиктивные переменные.
In [92]: df = pd.DataFrame({'A': ['a', 'b', 'a'], 'B': ['c', 'c', 'b'],
....: 'C': [1, 2, 3]})
....:
In [93]: pd.get_dummies(df)
Out[93]:
C A_a A_b B_b B_c
0 1 1 0 0 1
1 2 0 1 0 1
2 3 1 0 1 0
Все столбцы, не являющиеся строковыми, включаются в выходные данные без изменений.
Вы можете управлять столбцами, которые кодируются, с помощью ключевого слова columns.
In [94]: pd.get_dummies(df, columns=['A']) Out[94]: B C A_a A_b 0 c 1 1 0 1 c 2 0 1 2 b 3 1 0
Обратите внимание, что столбец B всё ещё включён в вывод, просто он не был закодирован. Вы можете удалить B перед вызовом get_dummies, если не хотите включать его в вывод.
Как и в версии для Series, вы можете передать значения для prefix и prefix_sep. По умолчанию имя столбца используется в качестве префикса, а «_» в качестве разделителя префикса. Вы можете указать prefix и prefix_sep тремя способами
- строка: используйте одно и то же значение для
prefixилиprefix_sepдля каждого кодируемого столбца - список: длина списка должна совпадать с количеством кодируемых столбцов.
- словарь: сопоставление имени столбца с префиксом
In [95]: simple = pd.get_dummies(df, prefix='new_prefix')
In [96]: simple
Out[96]:
C new_prefix_a new_prefix_b new_prefix_b new_prefix_c
0 1 1 0 0 1
1 2 0 1 0 1
2 3 1 0 1 0
In [97]: from_list = pd.get_dummies(df, prefix=['from_A', 'from_B'])
In [98]: from_list
Out[98]:
C from_A_a from_A_b from_B_b from_B_c
0 1 1 0 0 1
1 2 0 1 0 1
2 3 1 0 1 0
In [99]: from_dict = pd.get_dummies(df, prefix={'B': 'from_B', 'A': 'from_A'})
In [100]: from_dict
Out[100]:
C from_A_a from_A_b from_B_b from_B_c
0 1 1 0 0 1
1 2 0 1 0 1
2 3 1 0 1 0
Новая в версии 0.18.0.
Иногда полезно сохранить только k-1 уровней категориальной переменной, чтобы избежать коллинеарности при подаче результата в статистические модели. Вы можете переключиться на этот режим, включив drop_first.
In [101]: s = pd.Series(list('abcaa'))
In [102]: pd.get_dummies(s)
Out[102]:
a b c
0 1 0 0
1 0 1 0
2 0 0 1
3 1 0 0
4 1 0 0
In [103]: pd.get_dummies(s, drop_first=True)
Out[103]:
b c
0 0 0
1 1 0
2 0 1
3 0 0
4 0 0
Если столбец содержит только один уровень, он будет опущен в результате.
In [104]: df = pd.DataFrame({'A':list('aaaaa'),'B':list('ababc')})
In [105]: pd.get_dummies(df)
Out[105]:
A_a B_a B_b B_c
0 1 1 0 0
1 1 0 1 0
2 1 1 0 0
3 1 0 1 0
4 1 0 0 1
In [106]: pd.get_dummies(df, drop_first=True)
Out[106]:
B_b B_c
0 0 0
1 1 0
2 0 0
3 1 0
4 0 1
Факторизация значений
Для кодирования одномерных значений как перечислимого типа используйте factorize:
In [107]: x = pd.Series(['A', 'A', np.nan, 'B', 3.14, np.inf]) In [108]: x Out[108]: 0 A 1 A 2 NaN 3 B 4 3.14 5 inf dtype: object In [109]: labels, uniques = pd.factorize(x) In [110]: labels Out[110]: array([ 0, 0, -1, 1, 2, 3]) In [111]: uniques Out[111]: Index([u'A', u'B', 3.14, inf], dtype='object')
Обратите внимание, что factorize аналогично numpy.unique, но отличается обработкой NaN:
Примечание
Следующие numpy.unique не будут работать в Python 3 с TypeError из-за ошибки упорядочения. См. также Здесь
In [112]: pd.factorize(x, sort=True) Out[112]: (array([ 2, 2, -1, 3, 0, 1]), Index([3.14, inf, u'A', u'B'], dtype='object')) In [113]: np.unique(x, return_inverse=True)[::-1] Out[113]: (array([3, 3, 0, 4, 1, 2]), array([nan, 3.14, inf, 'A', 'B'], dtype=object))
Примечание
Если вы хотите обработать только один столбец как категориальную переменную (как фактор в R), вы можете использовать df["cat_col"] = pd.Categorical(df["col"]) или df["cat_col"] = df["col"].astype("category") Для полной документации по Categorical, см. Введение в категориальные типы и Документацию API. Эта функция была введена в версии 0.15.
© 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/reshaping.html