Como um usuário de Re python, já vi esse tipo de pergunta algumas vezes.
Em R, eles têm a função interna do pacote tidyrchamada unnest. Mas em Python( pandas) não há função incorporada para este tipo de pergunta.
Sei que as objectcolunas typesempre tornam os dados difíceis de converter com uma pandasfunção '. Quando recebi dados como esses, a primeira coisa que me veio à mente foi 'nivelar' ou desaninhar as colunas.
Estou usando pandase pythonfunções para este tipo de pergunta. Se você está preocupado com a velocidade das soluções acima, verifique a resposta do usuário3483203, pois ele está usando numpye na maioria das vezes numpyé mais rápido. Eu recomendo Cpythone numbase a velocidade é importante no seu caso.
Método 0 [pandas> = 0,25]
Começando com pandas 0,25 , se você só precisa explodir uma coluna, pode usar a explodefunção:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
Método 1
apply + pd.Series (fácil de entender, mas em termos de desempenho não recomendado).
df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0:'B'})
Out[463]:
A B
0 1 1
1 1 2
0 2 1
1 2 2
Método 2
Usando repeatcom o DataFrameconstrutor, recrie seu dataframe (bom em desempenho, não bom em colunas múltiplas)
df=pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})
df
Out[465]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
Método 2.1,
por exemplo, além de A, temos A.1 ..... An Se ainda usarmos o método ( Método 2 ) acima, será difícil recriar as colunas uma por uma.
Solução: joinou mergecom o indexdepois de 'unnest' as colunas individuais
s=pd.DataFrame({'B':np.concatenate(df.B.values)},index=df.index.repeat(df.B.str.len()))
s.join(df.drop('B',1),how='left')
Out[477]:
B A
0 1 1
0 2 1
1 1 2
1 2 2
Se você precisa da ordem das colunas exatamente como antes, adicione reindexno final.
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
Método 3
recriar olist
pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)
Out[488]:
A B
0 1 1
1 1 2
2 2 1
3 2 2
Se houver mais de duas colunas, use
s=pd.DataFrame([[x] + [z] for x, y in zip(df.index,df.B) for z in y])
s.merge(df,left_on=0,right_index=True)
Out[491]:
0 1 A B
0 0 1 1 [1, 2]
1 0 2 1 [1, 2]
2 1 1 2 [1, 2]
3 1 2 2 [1, 2]
Método 4
usando reindex ouloc
df.reindex(df.index.repeat(df.B.str.len())).assign(B=np.concatenate(df.B.values))
Out[554]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
#df.loc[df.index.repeat(df.B.str.len())].assign(B=np.concatenate(df.B.values))
Método 5
quando a lista contém apenas valores únicos:
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]]})
from collections import ChainMap
d = dict(ChainMap(*map(dict.fromkeys, df['B'], df['A'])))
pd.DataFrame(list(d.items()),columns=df.columns[::-1])
Out[574]:
B A
0 1 1
1 2 1
2 3 2
3 4 2
Método 6
usando numpypara alto desempenho:
newvalues=np.dstack((np.repeat(df.A.values,list(map(len,df.B.values))),np.concatenate(df.B.values)))
pd.DataFrame(data=newvalues[0],columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
Método 7
usando a função de base itertools cyclee chain: Solução Python pura apenas por diversão
from itertools import cycle,chain
l=df.values.tolist()
l1=[list(zip([x[0]], cycle(x[1])) if len([x[0]]) > len(x[1]) else list(zip(cycle([x[0]]), x[1]))) for x in l]
pd.DataFrame(list(chain.from_iterable(l1)),columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
Generalizando para várias colunas
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]],'C':[[1,2],[3,4]]})
df
Out[592]:
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4]
Função de autodefinição:
def unnesting(df, explode):
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
unnesting(df,['B','C'])
Out[609]:
B C A
0 1 1 1
0 2 2 1
1 3 3 2
1 4 4 2
Desaninhamento de colunas
Todo o método acima está falando sobre o desninhamento vertical e explodir, se você precisar expandir a lista horizontal , verifique com o pd.DataFrameconstrutor
df.join(pd.DataFrame(df.B.tolist(),index=df.index).add_prefix('B_'))
Out[33]:
A B C B_0 B_1
0 1 [1, 2] [1, 2] 1 2
1 2 [3, 4] [3, 4] 3 4
Função atualizada
def unnesting(df, explode, axis):
if axis==1:
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
else :
df1 = pd.concat([
pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
return df1.join(df.drop(explode, 1), how='left')
Resultado do teste
unnesting(df, ['B','C'], axis=0)
Out[36]:
B0 B1 C0 C1 A
0 1 2 1 2 1
1 3 4 3 4 2