Como dinamizar um dataframe?
- O que é pivô?
- Como faço para girar?
- Isso é um pivô?
- Formato longo para formato largo?
Já vi muitas perguntas sobre tabelas dinâmicas. Mesmo que eles não saibam que estão perguntando sobre tabelas dinâmicas, geralmente estão. É virtualmente impossível escrever uma pergunta canônica e uma resposta que englobe todos os aspectos da rotação ...
... Mas eu vou tentar.
O problema com as perguntas e respostas existentes é que frequentemente a pergunta se concentra em uma nuance que o OP tem dificuldade em generalizar a fim de usar várias das boas respostas existentes. No entanto, nenhuma das respostas tenta dar uma explicação abrangente (porque é uma tarefa difícil)
Veja alguns exemplos da minha pesquisa no google
- Boa pergunta e resposta. Mas a resposta responde apenas à pergunta específica com pouca explicação.
- Nesta questão, o OP está preocupado com a saída do pivô. Ou seja, a aparência das colunas. OP queria que fosse semelhante a R. Isso não é muito útil para usuários de pandas.
- Outra pergunta decente, mas a resposta se concentra em um método, a saber
pd.DataFrame.pivot
Assim, sempre que alguém pesquisa por pivot, obtém resultados esporádicos que provavelmente não responderão à sua pergunta específica.
Configuração
Você pode notar que nomeei claramente minhas colunas e os valores de coluna relevantes para corresponder com a forma como vou dinamizar as respostas abaixo.
import numpy as np
import pandas as pd
from numpy.core.defchararray import add
np.random.seed([3,1415])
n = 20
cols = np.array(['key', 'row', 'item', 'col'])
arr1 = (np.random.randint(5, size=(n, 4)) // [2, 1, 2, 1]).astype(str)
df = pd.DataFrame(
add(cols, arr1), columns=cols
).join(
pd.DataFrame(np.random.rand(n, 2).round(2)).add_prefix('val')
)
print(df)
key row item col val0 val1
0 key0 row3 item1 col3 0.81 0.04
1 key1 row2 item1 col2 0.44 0.07
2 key1 row0 item1 col0 0.77 0.01
3 key0 row4 item0 col2 0.15 0.59
4 key1 row0 item2 col1 0.81 0.64
5 key1 row2 item2 col4 0.13 0.88
6 key2 row4 item1 col3 0.88 0.39
7 key1 row4 item1 col1 0.10 0.07
8 key1 row0 item2 col4 0.65 0.02
9 key1 row2 item0 col2 0.35 0.61
10 key2 row0 item2 col1 0.40 0.85
11 key2 row4 item1 col2 0.64 0.25
12 key0 row2 item2 col3 0.50 0.44
13 key0 row4 item1 col4 0.24 0.46
14 key1 row3 item2 col3 0.28 0.11
15 key0 row3 item1 col1 0.31 0.23
16 key0 row0 item2 col3 0.86 0.01
17 key0 row4 item0 col3 0.64 0.21
18 key2 row2 item2 col0 0.13 0.45
19 key0 row2 item0 col4 0.37 0.70
Questões)
Porque eu consigo
ValueError: Index contains duplicate entries, cannot reshapeComo faço para dinamizar de
dfforma que oscolvalores sejam colunas, osrowvalores sejam o índice e a média deval0sejam os valores?col col0 col1 col2 col3 col4 row row0 0.77 0.605 NaN 0.860 0.65 row2 0.13 NaN 0.395 0.500 0.25 row3 NaN 0.310 NaN 0.545 NaN row4 NaN 0.100 0.395 0.760 0.24Como faço para dinamizar de
dfforma que oscolvalores sejam colunas, osrowvalores sejam o índice, a média dosval0valores e os valores ausentes sejam0?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.24Posso conseguir algo diferente de
mean, como talvezsum?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.24Posso fazer mais de uma agregação por vez?
sum mean col col0 col1 col2 col3 col4 col0 col1 col2 col3 col4 row row0 0.77 1.21 0.00 0.86 0.65 0.77 0.605 0.000 0.860 0.65 row2 0.13 0.00 0.79 0.50 0.50 0.13 0.000 0.395 0.500 0.25 row3 0.00 0.31 0.00 1.09 0.00 0.00 0.310 0.000 0.545 0.00 row4 0.00 0.10 0.79 1.52 0.24 0.00 0.100 0.395 0.760 0.24Posso agregar várias colunas de valor?
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.46Pode subdividir por várias colunas?
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.00Ou
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.00Posso agregar a frequência em que a coluna e as linhas ocorrem juntas, também conhecido como "tabulação cruzada"?
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 1Como faço para converter um DataFrame de longo para largo ao girar APENAS em duas colunas? Dado,
np.random.seed([3, 1415]) df2 = pd.DataFrame({'A': list('aaaabbbc'), 'B': np.random.choice(15, 8)}) df2 A B 0 a 0 1 a 11 2 a 2 3 a 11 4 b 10 5 b 10 6 b 14 7 c 7O esperado deve ser parecido com
a b c 0 0.0 10.0 7.0 1 11.0 10.0 NaN 2 2.0 14.0 NaN 3 11.0 NaN NaNComo faço para nivelar o índice múltiplo em índice único após
pivotDe
1 2 1 1 2 a 2 1 1 b 2 1 0 c 1 0 0Para
1|1 2|1 2|2 a 2 1 1 b 2 1 0 c 1 0 0
Respostas
Começamos respondendo à primeira pergunta:
Questão 1
Porque eu consigo
ValueError: Index contains duplicate entries, cannot reshape
Isso ocorre porque o pandas está tentando reindexar um objeto columnsou indexcom entradas duplicadas. Existem vários métodos a serem usados para realizar um pivô. Alguns deles não são adequados para quando há duplicatas das chaves nas quais está sendo solicitado o pivô. Por exemplo. Considere pd.DataFrame.pivot. Eu sei que há entradas duplicadas que compartilham os valores rowe col:
df.duplicated(['row', 'col']).any()
True
Então, quando eu pivotuso
df.pivot(index='row', columns='col', values='val0')
Recebo o erro mencionado acima. Na verdade, recebo o mesmo erro quando tento realizar a mesma tarefa com:
df.set_index(['row', 'col'])['val0'].unstack()
Aqui está uma lista de expressões idiomáticas que podemos usar para girar
pd.DataFrame.groupby+pd.DataFrame.unstack- Boa abordagem geral para fazer praticamente qualquer tipo de pivô
- Você especifica todas as colunas que constituirão os níveis de linha dinâmica e os níveis de coluna em um grupo por. Em seguida, selecione as colunas restantes que deseja agregar e a (s) função (ões) que deseja realizar a agregação. Finalmente, você
unstackos níveis que deseja que estejam no índice da coluna.
pd.DataFrame.pivot_table- Uma versão glorificada do
groupbycom API mais intuitiva. Para muitas pessoas, essa é a abordagem preferida. E é a abordagem pretendida pelos desenvolvedores. - Especifique nível de linha, níveis de coluna, valores a serem agregados e função (ões) para realizar agregações.
- Uma versão glorificada do
pd.DataFrame.set_index+pd.DataFrame.unstack- Conveniente e intuitivo para alguns (inclusive eu). Não é possível lidar com chaves agrupadas duplicadas.
- Semelhante ao
groupbyparadigma, especificamos todas as colunas que eventualmente serão níveis de linha ou coluna e as definimos como o índice. Em seguida,unstackos níveis que queremos nas colunas. Se os níveis de índice ou níveis de coluna restantes não forem exclusivos, esse método falhará.
pd.DataFrame.pivot- Muito semelhante a
set_indexporque compartilha a limitação de chave duplicada. A API também é muito limitada. Leva apenas valores escalares paraindex,columns,values. - Semelhante ao
pivot_tablemétodo, porque selecionamos linhas, colunas e valores nos quais fazer o pivô. No entanto, não podemos agregar e se as linhas ou colunas não forem exclusivas, esse método falhará.
- Muito semelhante a
pd.crosstab- Esta versão especializada
pivot_tablee em sua forma mais pura é a maneira mais intuitiva de realizar várias tarefas.
- Esta versão especializada
pd.factorize+np.bincount- Esta é uma técnica altamente avançada, muito obscura, mas muito rápida. Não pode ser usado em todas as circunstâncias, mas quando puder ser usado e você se sentir confortável em usá-lo, colherá os frutos do desempenho.
pd.get_dummies+pd.DataFrame.dot- Eu uso isso para executar habilmente a tabulação cruzada.
Exemplos
O que vou fazer para cada resposta e pergunta subsequente é respondê-la usando pd.DataFrame.pivot_table. Em seguida, fornecerei alternativas para realizar a mesma tarefa.
Questão 3
Como faço para dinamizar de
dfforma que oscolvalores sejam colunas, osrowvalores sejam o índice, a média dosval0valores e os valores ausentes sejam0?
pd.DataFrame.pivot_tablefill_valuenão é definido por padrão. Eu tendo a configurá-lo apropriadamente Nesse caso, eu o defini como0. Observe que pulei a pergunta 2 , pois é a mesma que esta resposta sem ofill_valueaggfunc='mean'é o padrão e não precisei defini-lo. Eu incluí para ser explícito.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.groupbydf.groupby(['row', 'col'])['val0'].mean().unstack(fill_value=0)pd.crosstabpd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc='mean').fillna(0)
Questão 4
Posso conseguir algo diferente de
mean, como talvezsum?
pd.DataFrame.pivot_tabledf.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.24pd.DataFrame.groupbydf.groupby(['row', 'col'])['val0'].sum().unstack(fill_value=0)pd.crosstabpd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc='sum').fillna(0)
Questão 5
Posso fazer mais de uma agregação por vez?
Observe que para pivot_tablee crosstabeu precisava passar a lista de chamáveis. Por outro lado, groupby.aggé capaz de receber strings para um número limitado de funções especiais. groupby.aggtambém teria usado os mesmos chamáveis que passamos para os outros, mas geralmente é mais eficiente aproveitar os nomes de função de string, pois há eficiências a serem obtidas.
pd.DataFrame.pivot_tabledf.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.24pd.DataFrame.groupbydf.groupby(['row', 'col'])['val0'].agg(['size', 'mean']).unstack(fill_value=0)pd.crosstabpd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc=[np.size, np.mean]).fillna(0, downcast='infer')
Questão 6
Posso agregar várias colunas de valor?
pd.DataFrame.pivot_tablenós passamos,values=['val0', 'val1']mas poderíamos ter deixado isso completamente de ladodf.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.46pd.DataFrame.groupbydf.groupby(['row', 'col'])['val0', 'val1'].mean().unstack(fill_value=0)
Questão 7
Pode subdividir por várias colunas?
pd.DataFrame.pivot_tabledf.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.00pd.DataFrame.groupbydf.groupby( ['row', 'item', 'col'] )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
Questão 8
Pode subdividir por várias colunas?
pd.DataFrame.pivot_tabledf.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.00pd.DataFrame.groupbydf.groupby( ['key', 'row', 'item', 'col'] )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)pd.DataFrame.set_indexporque o conjunto de chaves é único para linhas e colunasdf.set_index( ['key', 'row', 'item', 'col'] )['val0'].unstack(['item', 'col']).fillna(0).sort_index(1)
Questão 9
Posso agregar a frequência em que a coluna e as linhas ocorrem juntas, também conhecido como "tabulação cruzada"?
pd.DataFrame.pivot_tabledf.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 1pd.DataFrame.groupbydf.groupby(['row', 'col'])['val0'].size().unstack(fill_value=0)pd.crosstabpd.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 1pd.get_dummiespd.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
Questão 10
Como faço para converter um DataFrame de longo para largo girando APENAS em duas colunas?
A primeira etapa é atribuir um número a cada linha - esse número será o índice da linha desse valor no resultado dinamizado. Isso é feito usando 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
A segunda etapa é usar a coluna recém-criada como o índice a ser chamado 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
Questão 11
Como faço para nivelar o índice múltiplo em índice único após
pivot
Se columnsdigitar objectcom stringjoin
df.columns = df.columns.map('|'.join)
outro format
df.columns = df.columns.map('{0[0]}|{0[1]}'.format)
Para estender a resposta de @ piRSquared a outra versão da Questão 10
Questão 10.1
Quadro de dados:
d = data = {'A': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 3, 6: 5},
'B': {0: 'a', 1: 'b', 2: 'c', 3: 'a', 4: 'b', 5: 'a', 6: 'c'}}
df = pd.DataFrame(d)
A B
0 1 a
1 1 b
2 1 c
3 2 a
4 2 b
5 3 a
6 5 c
Resultado:
0 1 2
A
1 a b c
2 a b None
3 a None None
5 c None None
Usando df.groupbyepd.Series.tolist
t = df.groupby('A')['B'].apply(list)
out = pd.DataFrame(t.tolist(),index=t.index)
out
0 1 2
A
1 a b c
2 a b None
3 a None None
5 c None None
Ou uma alternativa muito melhor usando pd.pivot_tablecomdf.squeeze.
t = df.pivot_table(index='A',values='B',aggfunc=list).squeeze()
out = pd.DataFrame(t.tolist(),index=t.index)