Ответ 1
Начнем с ответа на первый вопрос:
Вопрос 1
Почему я получаю
ValueError: Index contains duplicate entries, cannot reshape
Это происходит потому, что pandas пытается переиндексировать columns
или index
объект с повторяющимися записями. Существуют различные методы, которые могут использовать для разворота. Некоторые из них не очень подходят, когда есть дубликаты клавиш, в которых его просят повернуть. Например. Рассмотрим pd.DataFrame.pivot
. Я знаю, что есть повторяющиеся записи, которые разделяют значения row
и col
:
df.duplicated(['row', 'col']).any()
True
Поэтому, когда я pivot
с помощью
df.pivot(index='row', columns='col', values='val0')
Я получаю ошибку, упомянутую выше. Фактически, я получаю ту же ошибку, когда пытаюсь выполнить ту же задачу с:
df.set_index(['row', 'col'])['val0'].unstack()
Вот список идиом, которые мы можем использовать для поворота
-
pd.DataFrame.groupby
+pd.DataFrame.unstack
- Хороший общий подход для выполнения практически любого типа разворота
- Вы указываете все столбцы, которые будут составлять поворотные уровни строк и уровни столбцов в одной группе. Вы следуете за этим, выбирая оставшиеся столбцы, которые вы хотите агрегировать, и функции, которые вы хотите выполнить агрегацию. Наконец, вы
unstack
уровни, которые вы хотите видеть в индексе столбца.
-
pd.DataFrame.pivot_table
- Прославленная версия
groupby
с более интуитивным API. Для многих это предпочтительный подход. И это намеченный подход со стороны разработчиков. - Укажите уровень строки, уровни столбцов, значения для агрегирования и функции для выполнения агрегации.
- Прославленная версия
-
pd.DataFrame.set_index
+pd.DataFrame.unstack
- Удобный и интуитивно понятный для некоторых (включая меня). Не удается обработать дублированные сгруппированные ключи.
- Подобно парадигме
groupby
, мы указываем все столбцы, которые в конечном итоге будут уровнями строк или столбцов, и устанавливаем их в качестве индекса. Затем мыunstack
уровни, которые мы хотим в столбцах. Если оставшиеся уровни индекса или уровни столбца не являются уникальными, этот метод завершится ошибкой.
-
pd.DataFrame.pivot
- Очень похоже на
set_index
в том, что оно разделяет ограничение дубликата ключа. API также очень ограничен. Он принимает только скалярные значения дляindex
,columns
,values
. - Подобно методу
pivot_table
, мы выбираем строки, столбцы и значения для поворота. Однако мы не можем агрегировать, и если строки или столбцы не являются уникальными, этот метод завершится ошибкой.
- Очень похоже на
-
pd.crosstab
- Это специализированная версия
pivot_table
и в ее чистом виде это наиболее интуитивно понятный способ выполнения нескольких задач.
- Это специализированная версия
-
pd.factorize
+np.bincount
- Это очень продвинутая техника, которая очень непонятна, но очень быстра. Его нельзя использовать при любых обстоятельствах, но когда его можно использовать, и вы можете его использовать, вы получите плоды производительности.
-
pd.get_dummies
+pd.DataFrame.dot
- Я использую это для умного выполнения кросс-табуляции.
Примеры
Что я собираюсь сделать для каждого последующего ответа и вопроса, так это ответить на него с помощью pd.DataFrame.pivot_table
. Затем я предоставлю альтернативы для выполнения той же задачи.
Вопрос 3
Как мне повернуть
df
, чтобы значенияcol
были столбцами, значенияrow
были индексом, средние значенияval0
были значениями, а отсутствующие значения равны0
?
-
pd.DataFrame.pivot_table
-
fill_value
не устанавливается по умолчанию. Я склонен устанавливать это соответствующим образом. В этом случае я установил его на0
. Заметьте, что я пропустил вопрос 2, так как он совпадает с ответом безfill_value
-
aggfunc='mean'
- это значение по умолчанию, и мне не нужно было его устанавливать. Я включил это, чтобы быть явным.df.pivot_table( values='val0', index='row', columns='col', fill_value=0, aggfunc='mean') col col0 col1 col2 col3 col4 row row0 0.77 0.605 0.000 0.860 0.65 row2 0.13 0.000 0.395 0.500 0.25 row3 0.00 0.310 0.000 0.545 0.00 row4 0.00 0.100 0.395 0.760 0.24
-
-
pd.DataFrame.groupby
df.groupby(['row', 'col'])['val0'].mean().unstack(fill_value=0)
-
pd.crosstab
pd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc='mean').fillna(0)
Вопрос 4
Могу ли я получить что-то кроме
mean
, например,sum
?
-
pd.DataFrame.pivot_table
df.pivot_table( values='val0', index='row', columns='col', fill_value=0, aggfunc='sum') col col0 col1 col2 col3 col4 row row0 0.77 1.21 0.00 0.86 0.65 row2 0.13 0.00 0.79 0.50 0.50 row3 0.00 0.31 0.00 1.09 0.00 row4 0.00 0.10 0.79 1.52 0.24
-
pd.DataFrame.groupby
df.groupby(['row', 'col'])['val0'].sum().unstack(fill_value=0)
-
pd.crosstab
pd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc='sum').fillna(0)
Вопрос 5
Могу ли я сделать больше одной агрегации за раз?
Обратите внимание, что для pivot_table
и cross_tab
мне нужно было передать список вызываемых объектов. С другой стороны, groupby.agg
может принимать строки для ограниченного числа специальных функций. groupby.agg
также взял бы те же самые вызовы, которые мы передали другим, но зачастую эффективнее использовать имена строковых функций, поскольку есть преимущества, которые нужно получить.
-
pd.DataFrame.pivot_table
df.pivot_table( values='val0', index='row', columns='col', fill_value=0, aggfunc=[np.size, np.mean]) size mean col col0 col1 col2 col3 col4 col0 col1 col2 col3 col4 row row0 1 2 0 1 1 0.77 0.605 0.000 0.860 0.65 row2 1 0 2 1 2 0.13 0.000 0.395 0.500 0.25 row3 0 1 0 2 0 0.00 0.310 0.000 0.545 0.00 row4 0 1 2 2 1 0.00 0.100 0.395 0.760 0.24
-
pd.DataFrame.groupby
df.groupby(['row', 'col'])['val0'].agg(['size', 'mean']).unstack(fill_value=0)
-
pd.crosstab
pd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc=[np.size, np.mean]).fillna(0, downcast='infer')
Вопрос 6
Могу ли я объединить несколько столбцов значений?
-
pd.DataFrame.pivot_table
мы передаемvalues=['val0', 'val1']
но мы могли бы полностьюvalues=['val0', 'val1']
df.pivot_table( values=['val0', 'val1'], index='row', columns='col', fill_value=0, aggfunc='mean') val0 val1 col col0 col1 col2 col3 col4 col0 col1 col2 col3 col4 row row0 0.77 0.605 0.000 0.860 0.65 0.01 0.745 0.00 0.010 0.02 row2 0.13 0.000 0.395 0.500 0.25 0.45 0.000 0.34 0.440 0.79 row3 0.00 0.310 0.000 0.545 0.00 0.00 0.230 0.00 0.075 0.00 row4 0.00 0.100 0.395 0.760 0.24 0.00 0.070 0.42 0.300 0.46
-
pd.DataFrame.groupby
df.groupby(['row', 'col'])['val0', 'val1'].mean().unstack(fill_value=0)
Вопрос 7
Можно разделить на несколько столбцов?
-
pd.DataFrame.pivot_table
df.pivot_table( values='val0', index='row', columns=['item', 'col'], fill_value=0, aggfunc='mean') item item0 item1 item2 col col2 col3 col4 col0 col1 col2 col3 col4 col0 col1 col3 col4 row row0 0.00 0.00 0.00 0.77 0.00 0.00 0.00 0.00 0.00 0.605 0.86 0.65 row2 0.35 0.00 0.37 0.00 0.00 0.44 0.00 0.00 0.13 0.000 0.50 0.13 row3 0.00 0.00 0.00 0.00 0.31 0.00 0.81 0.00 0.00 0.000 0.28 0.00 row4 0.15 0.64 0.00 0.00 0.10 0.64 0.88 0.24 0.00 0.000 0.00 0.00
-
pd.DataFrame.groupby
df.groupby( ['row', 'item', 'col'] )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
Вопрос 8
Можно разделить на несколько столбцов?
-
pd.DataFrame.pivot_table
df.pivot_table( values='val0', index=['key', 'row'], columns=['item', 'col'], fill_value=0, aggfunc='mean') item item0 item1 item2 col col2 col3 col4 col0 col1 col2 col3 col4 col0 col1 col3 col4 key row key0 row0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.86 0.00 row2 0.00 0.00 0.37 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.50 0.00 row3 0.00 0.00 0.00 0.00 0.31 0.00 0.81 0.00 0.00 0.00 0.00 0.00 row4 0.15 0.64 0.00 0.00 0.00 0.00 0.00 0.24 0.00 0.00 0.00 0.00 key1 row0 0.00 0.00 0.00 0.77 0.00 0.00 0.00 0.00 0.00 0.81 0.00 0.65 row2 0.35 0.00 0.00 0.00 0.00 0.44 0.00 0.00 0.00 0.00 0.00 0.13 row3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.28 0.00 row4 0.00 0.00 0.00 0.00 0.10 0.00 0.00 0.00 0.00 0.00 0.00 0.00 key2 row0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.40 0.00 0.00 row2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.13 0.00 0.00 0.00 row4 0.00 0.00 0.00 0.00 0.00 0.64 0.88 0.00 0.00 0.00 0.00 0.00
-
pd.DataFrame.groupby
df.groupby( ['key', 'row', 'item', 'col'] )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
-
pd.DataFrame.set_index
потому что набор ключей уникален как для строк, так и для столбцов.df.set_index( ['key', 'row', 'item', 'col'] )['val0'].unstack(['item', 'col']).fillna(0).sort_index(1)
Вопрос 9
Могу ли я объединить частоту, с которой столбец и строки встречаются вместе, то есть "кросс-табуляция"?
-
pd.DataFrame.pivot_table
df.pivot_table(index='row', columns='col', fill_value=0, aggfunc='size') col col0 col1 col2 col3 col4 row row0 1 2 0 1 1 row2 1 0 2 1 2 row3 0 1 0 2 0 row4 0 1 2 2 1
-
pd.DataFrame.groupby
df.groupby(['row', 'col'])['val0'].size().unstack(fill_value=0)
-
pd.cross_tab
pd.crosstab(df['row'], df['col'])
-
pd.factorize
+np.bincount
# get integer factorization 'i' and unique values 'r' # for column ''row'' i, r = pd.factorize(df['row'].values) # get integer factorization 'j' and unique values 'c' # for column ''col'' j, c = pd.factorize(df['col'].values) # 'n' will be the number of rows # 'm' will be the number of columns n, m = r.size, c.size # 'i * m + j' is a clever way of counting the # factorization bins assuming a flat array of length # 'n * m'. Which is why we subsequently reshape as '(n, m)' b = np.bincount(i * m + j, minlength=n * m).reshape(n, m) # BTW, whenever I read this, I think 'Bean, Rice, and Cheese' pd.DataFrame(b, r, c) col3 col2 col0 col1 col4 row3 2 0 0 1 0 row2 1 2 1 0 2 row0 1 0 1 2 1 row4 2 2 0 1 1
-
pd.get_dummies
pd.get_dummies(df['row']).T.dot(pd.get_dummies(df['col'])) col0 col1 col2 col3 col4 row0 1 2 0 1 1 row2 1 0 2 1 2 row3 0 1 0 2 0 row4 0 1 2 2 1
Вопрос 10
Как преобразовать DataFrame из длинного в широкий, поворачивая ТОЛЬКО на два столбца?
Первым шагом является присвоение номера каждой строке - этот номер будет индексом строки этого значения в сводном результате. Это делается с помощью GroupBy.cumcount
:
df2.insert(0, 'count', df.groupby('A').cumcount())
df2
count A B
0 0 a 0
1 1 a 11
2 2 a 2
3 3 a 11
4 0 b 10
5 1 b 10
6 2 b 14
7 0 c 7
Второй шаг - использовать вновь созданный столбец в качестве индекса для вызова DataFrame.pivot
.
df2.pivot(*df)
# df.pivot(index='count', columns='A', values='B')
A a b c
count
0 0.0 10.0 7.0
1 11.0 10.0 NaN
2 2.0 14.0 NaN
3 11.0 NaN NaN
Чтобы сделать это в одной строке, используйте assign
:
df2.assign(count=df.groupby('A').cumcount()).pivot('count', 'A', 'B')
A a b c
count
0 12.0 11.0 9.0
1 5.0 3.0 NaN
2 0.0 7.0 NaN
3 3.0 NaN NaN