359 Stimmen

Teilen (aufteilen) Sie den Zeichenfolgeneintrag des Pandas-Datenrahmens in separate Zeilen auf

Ich habe ein Pandas DataFrame, in dem eine Spalte mit Textzeichenfolgen Komma-getrennte Werte enthält. Ich möchte jedes CSV-Feld aufteilen und für jeden Eintrag eine neue Zeile erstellen (angenommen, dass das CSV sauber ist und nur auf ',' aufgeteilt werden muss). Zum Beispiel sollte a zu b werden:

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

Bisher habe ich verschiedene einfache Funktionen ausprobiert, aber die Methode .apply scheint nur einen Zeilenwert als Rückgabewert zu akzeptieren, wenn sie auf einer Achse verwendet wird, und ich kann .transform nicht zum Laufen bringen. Über jede Hilfe würde ich mich freuen!

Beispieldaten:

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}])

Ich weiß, dass dies nicht funktioniert, weil wir durch die Verwendung von NumPy die Meta-Daten des DataFrame verlieren, aber es sollte Ihnen eine Vorstellung davon geben, was ich zu tun versucht habe:

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)

3 Stimmen

Andere Lösungen auf dieser Seite funktionieren, aber ich fand die folgende kurz und effektiv. stackoverflow.com/questions/27263805/…

2 Stimmen

Für andere, die diese Seite besuchen und nach einer Lösung suchen, die mehrere Spalten beibehält, werfen Sie einen Blick auf diese Frage: stackoverflow.com/questions/17116814/…

19voto

Naga kiran Punkte 4378

Es besteht die Möglichkeit, das DataFrame aufzuteilen und zu explodieren, ohne die Struktur des DataFrames zu ändern

Aufteilen und Erweitern von Daten bestimmter Spalten

Eingabe:

    var1    var2
0   a,b,c   1
1   d,e,f   2

#Holen Sie sich die Indizes, die mit der Aufteilung wiederholt werden
df['var1'] = df['var1'].str.split(',')
df = df.explode('var1')

Ergebnis:

    var1    var2
0   a   1
0   b   1
0   c   1
1   d   2
1   e   2
1   f   2

Bearbeiten-1

Aufteilen und Erweitern von Zeilen für mehrere Spalten

Dateiname    RGB                                             RGB-Typ
0   A   [[0, 1650, 6, 39], [0, 1691, 1, 59], [50, 1402...   [r, g, b]
1   B   [[0, 1423, 16, 38], [0, 1445, 16, 46], [0, 141...   [r, g, b]

Neuindizierung basierend auf der Referenzspalte und Ausrichtung der Spaltenwertinformationen mit Stack

df = df.reindex(df.index.repeat(df['RGB-Typ'].apply(len)))
df = df.groupby('Dateiname').apply(lambda x:x.apply(lambda y: pd.Series(y.iloc[0])))
df.reset_index(drop=True).ffill()

Ergebnis:

                Dateiname    RGB-Typ    Top 1-Farbe    Top 1-Häufigkeit    Top 2-Farbe    Top 2-Häufigkeit
    Dateiname                            
 A  0       A   r   0   1650    6   39
    1       A   g   0   1691    1   59
    2       A   b   50  1402    49  187
 B  0       B   r   0   1423    16  38
    1       B   g   0   1445    16  46
    2       B   b   0   1419    16  39

19voto

inodb Punkte 4579

Ähnliche Frage wie: Pandas: Wie kann ich Text in einer Spalte in mehrere Zeilen aufteilen?

Du könntest Folgendes machen:

>> a=pd.DataFrame({"var1":"a,b,c d,e,f".split(),"var2":[1,2]})
>> s = a.var1.str.split(",").apply(pd.Series, 1).stack()
>> s.index = s.index.droplevel(-1)
>> del a['var1']
>> a.join(s)
   var2 var1
0     1    a
0     1    b
0     1    c
1     2    d
1     2    e
1     2    f

3 Stimmen

Es funktioniert, nachdem ein weiterer Umbenennungscode hinzugefügt wurde s.name = 'var1'

18voto

piRSquared Punkte 269273

Zusammenfassung

import pandas as pd
import numpy as np

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})

Demonstration

explode_str(a, 'var1', ',')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

Lassen Sie uns ein neues DataFrame d erstellen, das Listen enthält

d = a.assign(var1=lambda d: d.var1.str.split(','))

explode_list(d, 'var1')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

Allgemeine Kommentare

Ich werde np.arange mit repeat verwenden, um DataFrame-Indexpositionen zu erstellen, die ich mit iloc verwenden kann.

FAQ

Warum benutze ich nicht loc?

Weil der Index möglicherweise nicht eindeutig ist und die Verwendung von loc jede Zeile zurückgibt, die einem abgefragten Index entspricht.

Warum benutzen Sie nicht das values-Attribut und schneiden es?

Beim Aufrufen von values wird, wenn der gesamte DataFrame in einem zusammenhängenden "Block" vorliegt, Pandas eine Ansicht des Arrays zurückgeben, das den "Block" darstellt. Andernfalls muss Pandas ein neues Array zusammenstellen. Wenn Pandas das Array zusammensetzt, muss das Array eine einheitliche dtype haben. Häufig bedeutet dies, dass ein Array mit dtype object zurückgegeben wird. Durch die Verwendung von iloc anstelle des Schneidens des values-Attributs erspare ich mir die Arbeit damit umzugehen.

Warum verwenden Sie assign?

Wenn ich assign mit dem gleichen Spaltennamen verwende, den ich aufschlüssel, überschreibe ich die vorhandene Spalte und behalte ihre Position im DataFrame bei.

Warum sind die Indexwerte wiederholt?

Durch die Verwendung von iloc an wiederholten Positionen zeigt der resultierende Index das gleiche wiederholte Muster. Eine Wiederholung für jedes Element der Liste oder des Strings.
Dies kann mit reset_index(drop=True) zurückgesetzt werden.


Für Strings

Ich möchte die Strings nicht vorzeitig aufteilen müssen. Deshalb zähle ich stattdessen die Vorkommen des sep-Arguments unter der Annahme, dass die Länge der resultierenden Liste um eins größer wäre als die Anzahl der Trennzeichen.

Dann verwende ich dieses sep, um die Strings zu verbinden und dann aufzuteilen.

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

Für Listen

Ähnlich wie bei Strings, außer dass ich die Vorkommen von sep nicht zählen muss, da es bereits aufgeteilt ist.

Ich benutze Numpys concatenate, um die Listen zusammenzufügen.

import pandas as pd
import numpy as np

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})

0 Stimmen

Ich mag dieses hier. Wirklich prägnant und die Leistung sollte auch wirklich gut sein. Eine Frage jedoch: Ist df.iloc[i] dasselbe wie das Wiederholen von Zeilen des Datenrahmens oder effizienter als das? Vielen Dank!

6voto

jlln Punkte 133

Ich habe eine Lösung für Datenrahmen mit beliebigen Anzahlen von Spalten gefunden (während immer noch nur die Einträge einer Spalte gleichzeitig getrennt werden).

def splitDataFrameList(df,target_column,separator):
    ''' df = Datenrahmen zum Aufteilen,
    target_column = die Spalte, die die Werte zum Aufteilen enthält
    separator = das Symbol, das verwendet wird, um das Aufteilen durchzuführen

    gibt zurück: einen Datenrahmen mit jedem Eintrag für die Zielspalte getrennt, wobei jedes Element in eine neue Zeile verschoben wird. 
    Die Werte in den anderen Spalten sind über die neu aufgeteilten Zeilen dupliziert.
    '''
    def splitListToRows(row,row_accumulator,target_column,separator):
        split_row = row[target_column].split(separator)
        for s in split_row:
            new_row = row.to_dict()
            new_row[target_column] = s
            row_accumulator.append(new_row)
    new_rows = []
    df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
    new_df = pandas.DataFrame(new_rows)
    return new_df

3 Stimmen

Schön, aber leider langsam wegen dieser todict() Umwandlung :(

4voto

1'' Punkte 25004

Einzeiler mit split(___, expand=True) und den level und name Argumenten für reset_index():

>>> b = a.var1.str.split(',', expand=True).set_index(a.var2).stack().reset_index(level=0, name='var1')
>>> b
   var2 var1
0     1    a
1     1    b
2     1    c
0     2    d
1     2    e
2     2    f

Wenn du möchtest, dass b genauso aussieht wie in der Frage, kannst du zusätzlich Folgendes tun:

>>> b = b.reset_index(drop=True)[['var1', 'var2']]
>>> b
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X