Eu tenho um pandas dataframe
em que uma coluna de seqüências de texto contém valores separados por vírgula. Quero dividir cada campo CSV e criar uma nova linha por entrada (suponha que o CSV esteja limpo e só precise ser dividido em ','). Por exemplo, a
deve se tornar b
:
In [7]: a
Out[7]:
var1 var2
0 a,b,c 1
1 d,e,f 2
In [8]: b
Out[8]:
var1 var2
0 a 1
1 b 1
2 c 1
3 d 2
4 e 2
5 f 2
Até agora, tentei várias funções simples, mas o .apply
método parece aceitar apenas uma linha como valor de retorno quando usado em um eixo, e não consigo .transform
trabalhar. Qualquer sugestão será muito bem-vinda!
Dados de exemplo:
from pandas import DataFrame
import numpy as np
a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
{'var1': 'd,e,f', 'var2': 2}])
b = DataFrame([{'var1': 'a', 'var2': 1},
{'var1': 'b', 'var2': 1},
{'var1': 'c', 'var2': 1},
{'var1': 'd', 'var2': 2},
{'var1': 'e', 'var2': 2},
{'var1': 'f', 'var2': 2}])
Sei que isso não funcionará porque perdemos os metadados do DataFrame passando por numpy, mas isso deve lhe dar uma idéia do que tentei:
def fun(row):
letters = row['var1']
letters = letters.split(',')
out = np.array([row] * len(letters))
out['var1'] = letters
a['idx'] = range(a.shape[0])
z = a.groupby('idx')
z.transform(fun)
Respostas:
Que tal algo como isso:
Então você só precisa renomear as colunas
fonte
UPDATE2: função vetorizada mais genérica, que funcionará para várias
normal
e múltiplaslist
colunasDemo:
Várias
list
colunas - todas aslist
colunas devem ter o mesmo número de elementos em cada linha:preservando os valores do índice original:
Configuração:
Coluna CSV:
usando este pequeno truque, podemos converter colunas semelhantes a CSV em
list
colunas:ATUALIZAR: abordagem vetorizada genérica (funcionará também para várias colunas):
DF original:
Solução:
primeiro vamos converter strings CSV em listas:
Agora podemos fazer isso:
Resposta ANTIGA:
Inspirado na solução @AFinkelstein , eu queria torná-lo um pouco mais generalizado, que poderia ser aplicado ao DF com mais de duas colunas e tão rápido, quase quase, tão rápido quanto a solução da AFinkelstein):
fonte
.explode()
método na API (veja também esta resposta ).Após uma dolorosa experimentação para encontrar algo mais rápido que a resposta aceita, consegui que isso funcionasse. Ele foi executado 100x mais rápido no conjunto de dados em que eu experimentei.
Se alguém souber uma maneira de tornar isso mais elegante, modifique meu código. Não consegui encontrar uma maneira de funcionar sem definir as outras colunas que você deseja manter como índice e, em seguida, redefinir o índice e renomear as colunas, mas imagino que exista outra coisa que funcione.
fonte
TypeError: object of type 'float' has no len()
na primeira etapa (DataFrame(df.var1.str.split(',').tolist())
)NaN
nessa coluna; portanto, a substituição éb = DataFrame(a.var1.str.split(',').values.tolist(), index=a.var2).stack()
Aqui está uma função que escrevi para esta tarefa comum. É mais eficiente que os métodos
Series
/stack
. A ordem e os nomes das colunas são mantidos.Com esta função, a pergunta original é tão simples quanto:
fonte
Pandas> = 0,25
Os métodos Series e DataFrame definem um
.explode()
método que explode listas em linhas separadas. Consulte a seção de documentos em Explodindo uma coluna do tipo lista .Como você tem uma lista de sequências separadas por vírgula, divida a sequência em vírgula para obter uma lista de elementos e chame
explode
a coluna.Observe que
explode
funciona apenas em uma única coluna (por enquanto).NaNs e listas vazias recebem o tratamento que merecem sem que você precise pular os aros para acertar.
Essa é uma séria vantagem sobre as soluções baseadas em
ravel
+repeat
(que ignoram completamente as listas vazias e bloqueiam os NaNs).fonte
Pergunta semelhante a: pandas: como faço para dividir o texto em uma coluna em várias linhas?
Você poderia fazer:
fonte
s.name = 'var1'
TL; DR
Demonstração
Vamos criar um novo quadro de dados
d
que tenha listasComentários gerais
Vou usar
np.arange
comrepeat
para produzir posições de índice de quadro de dados que eu possa usariloc
.Perguntas frequentes
Por que eu não uso
loc
?Como o índice pode não ser exclusivo e usar
loc
retornará todas as linhas que correspondem a um índice consultado.Por que você não usa o
values
atributo e o divide?Ao chamar
values
, se a totalidade do quadro de dados estiver em um "bloco" coeso, o Pandas retornará uma visão da matriz que é o "bloco". Caso contrário, os pandas precisarão criar uma nova matriz. Ao pavimentar, essa matriz deve ser de um tipo uniforme. Frequentemente, isso significa retornar uma matriz com o tipo dtypeobject
. Usando emiloc
vez de cortar ovalues
atributo, eu me alivio de ter que lidar com isso.Por que você usa
assign
?Quando eu uso
assign
o mesmo nome de coluna que estou explodindo, sobrescrevo a coluna existente e mantenho sua posição no quadro de dados.Por que os valores do índice se repetem?
Em virtude do uso
iloc
em posições repetidas, o índice resultante mostra o mesmo padrão repetido. Uma repetição para cada elemento da lista ou sequência.Isso pode ser redefinido com
reset_index(drop=True)
For Strings
Eu não quero ter que dividir as cordas prematuramente. Então, em vez disso, conto as ocorrências do
sep
argumento assumindo que, se eu fosse dividido, o comprimento da lista resultante seria um a mais que o número de separadores.Eu, então, usar isso
sep
parajoin
as cordas, em seguidasplit
.Para listas
Semelhante ao das strings, exceto que eu não preciso contar ocorrências de
sep
porque já está dividido.Eu uso o Numpy
concatenate
para juntar as listas.fonte
Existe a possibilidade de dividir e explodir o quadro de dados sem alterar a estrutura do quadro de dados
Entrada:
Fora:
Edit-1
Indexar novamente com base na coluna de referência e alinhar as informações do valor da coluna com a pilha
Fora:
fonte
Eu vim com uma solução para quadros de dados com números arbitrários de colunas (enquanto ainda separava apenas as entradas de uma coluna por vez).
fonte
Aqui está uma mensagem bastante direta que usa o
split
método dos pandasstr
acessador e, em seguida, usa o NumPy para achatar cada linha em uma única matriz.Os valores correspondentes são recuperados repetindo a coluna não dividida o número correto de vezes com
np.repeat
.fonte
Eu tenho lutado com a experiência de falta de memória usando várias maneiras de explodir minhas listas, então preparei alguns parâmetros de referência para me ajudar a decidir quais respostas aprovar. Testei cinco cenários com proporções variadas do tamanho da lista com o número de listas. Compartilhando os resultados abaixo:
Tempo: (quanto menos, melhor, clique para ver a versão ampliada)
Pico de uso da memória: (menos é melhor)
Conclusões :
Detalhes completos (funções e código de benchmarking) estão nesta essência do GitHub . Observe que o problema do benchmark foi simplificado e não incluiu a divisão de strings na lista - que a maioria das soluções executava de maneira semelhante.
fonte
Com base na excelente solução do @ DMulligan , aqui está uma função genérica vetorizada (sem loops) que divide uma coluna de um quadro de dados em várias linhas e a funde de volta ao quadro de dados original. Ele também usa uma ótima
change_column_order
função genérica dessa resposta .Exemplo:
Observe que ele preserva o índice original e a ordem das colunas. Também funciona com quadros de dados que possuem índice não sequencial.
fonte
A divisão da função string pode usar um argumento booleano de opção 'expand'.
Aqui está uma solução usando este argumento:
fonte
Acabei de usar a excelente resposta de jiln de cima, mas precisava expandir para dividir várias colunas. Pensei em compartilhar.
fonte
atualizou a resposta do MaxU com suporte a MultiIndex
fonte
Uso de uma linha
split(___, expand=True)
e os argumentoslevel
ename
parareset_index()
:Se você precisar
b
se parecer exatamente com a pergunta, poderá fazer adicionalmente:fonte
Eu vim com a seguinte solução para esse problema:
fonte
Outra solução que usa pacote de cópias python
fonte
Há muitas respostas aqui, mas estou surpreso que ninguém tenha mencionado a função de explosão dos pandas incorporados. Confira o link abaixo: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html#pandas.DataFrame.explode
Por alguma razão, não consegui acessar essa função, então usei o código abaixo:
Acima está uma amostra dos meus dados. Como você pode ver as pessoas coluna tinha uma série de pessoas, e eu estava tentando explodir. O código que eu dei funciona para dados do tipo lista. Portanto, tente colocar os dados de texto separados por vírgula no formato de lista. Além disso, como meu código usa funções integradas, é muito mais rápido que as funções custom / apply.
Nota: Pode ser necessário instalar o pandas_explode com o pip.
fonte
Eu tive um problema semelhante, minha solução foi converter o dataframe em uma lista de dicionários primeiro e depois fazer a transição. Aqui está a função:
Exemplo:
Você também pode alterar um pouco a função para oferecer suporte à separação de linhas do tipo lista.
fonte