2017-08-24 4 views
2

Ich habe einen Datenrahmen wie diese:Wie erstellt man effizient eine Pivot-Tabelle?

import pandas as pd 

df = pd.DataFrame({"c0": list('ABC'), 
        "c1": [" ".join(list('ab')), " ".join(list('def')), " ".join(list('s'))], 
        "c2": list('DEF')}) 

    c0  c1 c2 
0 A a b D 
1 B d e f E 
2 C  s F 

Ich mag eine Pivot-Tabelle erstellen, die wie folgt aussieht:

 c2 
c0 c1 
A a D 
    b D 
B d E 
    e E 
    f E 
C s F 

So werden die Einträge in c1 geteilt und dann als einzelnen Elemente behandelt in einem Multiindex verwendet.

Ich tue dies wie folgt:

newdf = pd.DataFrame() 

for indi, rowi in df.iterrows(): 

    # get all single elements in string 
    n_elements = rowi['c1'].split() 

    # only one element so we can just add the entire row 
    if len(n_elements) == 1: 
     newdf = newdf.append(rowi) 
    # more than one element 
    else: 
     for eli in n_elements: 
      # that allows to add new elements using loc, without it we will have identical index values 
      if not newdf.empty: 
       newdf = newdf.reset_index(drop=True) 
       newdf.index = -1 * newdf.index - 1 

      # add entire row 
      newdf = newdf.append(rowi) 
      # replace the entire string by the single element 
      newdf.loc[indi, 'c1'] = eli 

print newdf.reset_index(drop=True) 

die liefert

c0 c1 c2 
0 A a D 
1 A b D 
2 B d E 
3 B e E 
4 B f E 
5 C s F 

Dann kann ich einfach anrufen

pd.pivot_table(newdf, index=['c0', 'c1'], aggfunc=lambda x: ' '.join(set(str(v) for v in x))) 

, die mir den gewünschten Ausgang gibt (siehe oben).

Für riesige Datenframes, die ziemlich langsam sein können, frage ich mich, ob es eine effizientere Möglichkeit gibt, dies zu tun.

Antwort

3

Option 1

import numpy as np, pandas as pd 

s = df.c1.str.split() 
l = s.str.len() 
newdf = df.loc[df.index.repeat(l)].assign(c1=np.concatenate(s)).set_index(['c0', 'c1']) 
newdf 

     c2 
c0 c1 
A a D 
    b D 
B d E 
    e E 
    f E 
C s F 

Option 2
Sollte schneller

import numpy as np, pandas as pd 

s = np.core.defchararray.split(df.c1.values.astype(str), ' ') 
l = [len(x) for x in s.tolist()] 
r = np.arange(len(s)).repeat(l) 
i = pd.MultiIndex.from_arrays([ 
    df.c0.values[r], 
    np.concatenate(s) 
], names=['c0', 'c1']) 
newdf = pd.DataFrame({'c2': df.c2.values[r]}, i) 
newdf 

     c2 
c0 c1 
A a D 
    b D 
B d E 
    e E 
    f E 
C s F 
+0

Sehr nett (upvoted) und noch lesbar! Nur der Vollständigkeit halber: Sie könnten die Zeile "import numpy as np" hinzufügen und sie "newdf" zuweisen. – Cleb

+0

Hallo Pir, Es braucht Zeit, um durchzulesen und herauszufinden, wie effizient Ihre Methode ist! Danke für das Teilen ~ – Wen

2

So bekomme ich das Ergebnis, In R heißt es unnest.

df.c1=df.c1.apply(lambda x : pd.Series(x).str.split(' ')) 

df.set_index(['c0', 'c2'])['c1'].apply(pd.Series).stack().reset_index().drop('level_2',1).rename(columns={0:'c1'}).set_index(['c0','c1']) 


Out[208]: 
     c2 
c0 c1 
A a D 
    b D 
B d E 
    e E 
    f E 
C s F 
+1

Adaequat (upvoted), aber wird eine Weile brauchen, um durch es;). Nur Kosmetik: Sie können einfach 'split()' verwenden. – Cleb

+0

@Cleb Ja du hast Recht ~ :) – Wen

Verwandte Themen