2017-12-27 2 views
4

Ich habe mehrere ziemlich große Datenframes (> 1 Million Zeilen). In einer Spalte sind Strings unterschiedlicher Länge. Ich möchte diese Strings in einzelne Zeichen aufteilen, wobei jedes einzelne Zeichen in einer Spalte platziert wird.Effizient konvertieren Sie eine Spalte von Strings in mehrere Spalten von einzelnen Zeichen in Pandas

Ich kann dies tun mit pd.DataFrame.apply() - siehe unten - aber es ist viel zu langsam, um praktisch zu verwenden (und es hat auch eine Tendenz, den Kernal abzustürzen).

import pandas as pd 

df = pd.DataFrame(['AAVFD','TYU?W_Z', 'SomeOtherString', 'ETC.'], columns = ['One']) 

print df 
    One 
0 AAVFD 
1 TYU?W_Z 
2 SomeOtherString 
3 ETC. 

Convert Strings Listen unterschiedlicher Länge:

S1 = df.One.apply(list) 
print S1 
0         [A, A, V, F, D] 
1       [T, Y, U, ?, W, _, Z] 
2 [S, o, m, e, O, t, h, e, r, S, t, r, i, n, g] 
3          [E, T, C, .] 
Name: One, dtype: object 

jedes einzelne Zeichen in eine Spalte Put:

df2 = pd.DataFrame(S1.values.tolist()) 
print df2 
    0 1 2 3  4  5  6  7  8  9 10 11 12 13 \ 
0 A A V F  D None None None None None None None None None 
1 T Y U ?  W  _  Z None None None None None None None 
2 S o m e  O  t  h  e  r  S  t  r  i  n 
3 E T C . None None None None None None None None None None 

    14 
0 None 
1 None 
2  g 
3 None 

Leider ist dies sehr langsam. Es scheint, als ob ich in der Lage sein sollte, dies irgendwie zu vektorisieren, indem ich mich direkt mit dem numpy-Array befasse, das der df.One-Spalte zugrunde liegt. Allerdings, wenn ich es versucht habe, denke ich, dass es Schwierigkeiten mit der Tatsache hat, dass die Saiten in der Länge variieren.

Antwort

2

Ich weiß kaum, pandas aber die numpy Seite der Operation kann wie so erfolgen (auf Python 3; 'S1' anstelle von 'U1' auf Python 2 verwenden):

npchrs = df.values.astype(str).view('U1') 
# array([['A', 'A', 'V', 'F', 'D', '', '', '', '', '', '', '', '', '', ''], 
#  ['T', 'Y', 'U', '?', 'W', '_', 'Z', '', '', '', '', '', '', '', ''], 
#  ['S', 'o', 'm', 'e', 'O', 't', 'h', 'e', 'r', 'S', 't', 'r', 'i', 'n', 'g'], 
#  ['E', 'T', 'C', '.', '', '', '', '', '', '', '', '', '', '', '']], 
#  dtype='<U1') 

Wenn Sie ok mit leeren Zeichenfolgen anstelle von None s oder wenn sie in pandas ersetzen einfach ist, können Sie dies zurück in DF konvertieren und fertig sein.

Gemäß den Zeitvorgaben von @ COLDSPEED ist der folgende Schritt langsam, also wäre es besser, wenn Sie es vermeiden könnten. Falls nicht:

npobjs = npchrs.astype(object) 
npobjs[npobjs==''] = None 
# array([['A', 'A', 'V', 'F', 'D', None, None, None, None, None, None, None, 
#   None, None, None], 
#  ['T', 'Y', 'U', '?', 'W', '_', 'Z', None, None, None, None, None, 
#   None, None, None], 
#  ['S', 'o', 'm', 'e', 'O', 't', 'h', 'e', 'r', 'S', 't', 'r', 'i', 'n', 'g'], 
#  ['E', 'T', 'C', '.', None, None, None, None, None, None, None, None, 
#   None, None, None]], dtype=object) 
+0

Hmm, ich habe Probleme das erste Bit immer vielleicht zu arbeiten, weil ich bin mit Python 2? Ich bekomme "ValueError: neuer Typ nicht kompatibel mit Array." – Ben

+0

@Ben Ja, es gab diesen Unicode-Übergang, als sie zu Python3 wechselten. Könntest du bitte versuchen, ob 'S1' statt 'U1' für dich funktioniert? –

+0

Tatsächlich tut es das. Vielen Dank! – Ben

2

Eine Alternative eine Liste Verständnis mit, was ich denke, sollte ziemlich schnell sein -

df = pd.DataFrame([list(x) for x in df.One]) 
df 

    0 1 2 3  4  5  6  7  8  9  10 11 12 13 \ 
0 A A V F  D None None None None None None None None None 
1 T Y U ?  W  _  Z None None None None None None None 
2 S o m e  O  t  h  e  r  S  t  r  i  n 
3 E T C . None None None None None None None None None None 

    14 
0 None 
1 None 
2  g 
3 None 

Timings

df = pd.concat([df] * 10000, ignore_index=True) 
# original answer 
%timeit pd.DataFrame(df.One.apply(list).values.tolist()) 
10 loops, best of 3: 36.1 ms per loop 
# Paul Panzer's answer 
%%timeit 
npchrs = df.values.astype(str).view('U1') 
npobjs = npchrs.astype(object) 
npobjs[npobjs==''] = None 
pd.DataFrame(npobjs) 

10 loops, best of 3: 37.5 ms per loop 
# My list comp answer 
%timeit pd.DataFrame([list(x) for x in df.One.values]) 
10 loops, best of 3: 32.8 ms per loop 
# improved version of Paul Panzer's answer 
%timeit pd.DataFrame(df.values.astype(str).view('U1')) 
10 loops, best of 3: 20.1 ms per loop 

Haftungsausschluss - Timings variieren basierend auf den Daten, Python-Version, Umwelt und OS.

2

Hier ist ein Ansatz mit string-join, np.fromstring und masking (Idee von this post entlehnt) -

def join_mask(df): 
    lens = np.array([len(i) for i in df.One]) 
    n = lens.max() 
    out = np.full((len(df),n), None) 
    out[lens[:,None] > np.arange(n)] = np.fromstring(''.join(df.One), dtype='S1') 
    return pd.DataFrame(out) 

Probelauf -

In [160]: df 
Out[160]: 
       One 
0   AAVFD 
1   TYU?W_Z 
2 SomeOtherString 
3    ETC. 

In [161]: join_mask(df) 
Out[161]: 
    0 1 2 3  4  5  6  7  8  9  10 11 12 13 14 
0 A A V F  D None None None None None None None None None None 
1 T Y U ?  W  _  Z None None None None None None None None 
2 S o m e  O  t  h  e  r  S  t  r  i  n  g 
3 E T C . None None None None None None None None None None None 

Timings

Mit Timing-Setup des @ cᴏʟᴅsᴘᴇᴇᴅ auf nähert sich dem an produzieren die richtige None gefüllt Ausgabe df -

In [173]: df = pd.concat([df] * 10000, ignore_index=True) 

# original answer 
In [175]: %timeit pd.DataFrame(df.One.apply(list).values.tolist()) 
10 loops, best of 3: 27.2 ms per loop 

# @Paul Panzer's answer 
In [176]: %%timeit 
    ...: npchrs = df.values.astype(str).view('S1') 
    ...: npobjs = npchrs.astype(object) 
    ...: npobjs[npobjs==''] = None 
    ...: pd.DataFrame(npobjs) 
10 loops, best of 3: 20.3 ms per loop 

# @cᴏʟᴅsᴘᴇᴇᴅ's answer 
In [177]: %timeit pd.DataFrame([list(x) for x in df.One.values]) 
10 loops, best of 3: 27.6 ms per loop 

# Using solution in this post 
In [178]: %timeit join_mask(df) 
100 loops, best of 3: 13.8 ms per loop 
+0

Zukunft von NumPy :) https://www.youtube.com/watch?v=fowHwlpGb34 Sparse-Arrays, kategorische Daten, JIT kompiliert und viel Mehr! – kmario23

+1

@ kmario23 Schön! Danke für das Teilen. Würde es überprüfen. – Divakar

+0

'Linse = df.One.str.len(). Werte 'sollte schneller sein als die Liste comp (vielleicht). –

Verwandte Themen