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 reshape
Como faço para dinamizar de
df
forma que oscol
valores sejam colunas, osrow
valores sejam o índice e a média deval0
sejam 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.24
Como faço para dinamizar de
df
forma que oscol
valores sejam colunas, osrow
valores sejam o índice, a média dosval0
valores 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.24
Posso 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.24
Posso 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.24
Posso 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.46
Pode 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.00
Ou
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
Posso 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 1
Como 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 7
O 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 NaN
Como faço para nivelar o índice múltiplo em índice único após
pivot
De
1 2 1 1 2 a 2 1 1 b 2 1 0 c 1 0 0
Para
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 columns
ou index
com 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 row
e col
:
df.duplicated(['row', 'col']).any()
True
Então, quando eu pivot
uso
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ê
unstack
os níveis que deseja que estejam no índice da coluna.
pd.DataFrame.pivot_table
- Uma versão glorificada do
groupby
com 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
groupby
paradigma, especificamos todas as colunas que eventualmente serão níveis de linha ou coluna e as definimos como o índice. Em seguida,unstack
os 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_index
porque compartilha a limitação de chave duplicada. A API também é muito limitada. Leva apenas valores escalares paraindex
,columns
,values
. - Semelhante ao
pivot_table
mé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_table
e 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
df
forma que oscol
valores sejam colunas, osrow
valores sejam o índice, a média dosval0
valores e os valores ausentes sejam0
?
pd.DataFrame.pivot_table
fill_value
nã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_value
aggfunc='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.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)
Questão 4
Posso conseguir algo diferente de
mean
, como talvezsum
?
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)
Questão 5
Posso fazer mais de uma agregação por vez?
Observe que para pivot_table
e crosstab
eu 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.agg
també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_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')
Questão 6
Posso agregar várias colunas de valor?
pd.DataFrame.pivot_table
nó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.46
pd.DataFrame.groupby
df.groupby(['row', 'col'])['val0', 'val1'].mean().unstack(fill_value=0)
Questão 7
Pode subdividir por várias colunas?
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)
Questão 8
Pode subdividir por várias colunas?
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
porque 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_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.crosstab
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
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 columns
digitar object
com 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.groupby
epd.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_table
comdf.squeeze.
t = df.pivot_table(index='A',values='B',aggfunc=list).squeeze()
out = pd.DataFrame(t.tolist(),index=t.index)