2015-07-10 430 views
2

我有一個DataFrame中的列(這是一個csv中的列),這是一個逗號分隔值。我想將此列分成多列。pandas:將DataFrame列(一個系列)中的分隔值拆分爲多個列。優雅的解決方案

問題是一個老的問題,並且這裏也討論過了,但是有一個特點:一個條目可能是從0-n以逗號分隔的值。舉個例子:

df.head(): 

i: vals | sth_else 
--------------------- 
1: a,b,c | ba 
2: a,d | be 
3:  | bi 
4: e,a,c | bo 
5: e  | bu 

想我下面的輸出(或類似的,如真/假):

i : a | b | c | d | e | sth_else 
----------------------------------- 
1: 1 | 1 | 1 | 0 | 0 | ba 
2: 1 | 0 | 0 | 1 | 0 | be 
3: 0 | 0 | 0 | 0 | 0 | bi 
4: 1 | 0 | 1 | 0 | 1 | bo 
5: 0 | 0 | 0 | 0 | 1 | bu 

我目前正在與Series.str.split,然後Series.to_dict功能試驗,但出來任何令人滿意的結果(始終導致一個ValueError: arrays must all be same length。:)

此外,我總是試圖找到優雅的解決方案,幾個月後看着很容易理解;)。無論如何,高度讚賞命題!

以下是用於測試的dummy.csv

vals;sth_else 
a,b,c;ba 
a,d;be 
;bi 
e,a,c;bo 
e;bu 
+2

對於那些誰將會在下面的答案跌倒,我想這裏的答案是嚴格優越的:http://stackoverflow.com/questions/28121682/quickest-way-to-make-a-get-dummies-type-dataframe-from-a-column-with-a-multiple –

+0

@ AlexPetralia確實如此!這是真正的熊貓的方式 - 你爲什麼不把它也添加到這裏的答案? – dmeu

回答

2
import pandas as pd 
from StringIO import StringIO # py2.7 used here 
# from io.StringIO import StringIO if you have py3.x 

# data 
# ================================================================== 
csv_buffer = 'vals;sth_else\na,b,c;ba\na,d;be\n;bi\ne,a,c;bo\ne;bu' 

df = pd.read_csv(StringIO(csv_buffer), sep=';') 

Out[58]: 
    vals sth_else 
0 a,b,c  ba 
1 a,d  be 
2 NaN  bi 
3 e,a,c  bo 
4  e  bu 

# processing 
# ================================================================== 
def func(group): 
    return pd.Series(group.vals.str.split(',').values[0], name='vals') 

ser = df.groupby(level=0).apply(func) 

Out[60]: 
0 0  a 
    1  b 
    2  c 
1 0  a 
    1  d 
2 0 NaN 
3 0  e 
    1  a 
    2  c 
4 0  e 
Name: vals, dtype: object 


# use get_dummies, and then aggregate for each column of a b c d e to be its max (max is always 1 in this case) 
pd.get_dummies(ser) 

Out[85]: 
    a b c d e 
0 0 1 0 0 0 0 
    1 0 1 0 0 0 
    2 0 0 1 0 0 
1 0 1 0 0 0 0 
    1 0 0 0 1 0 
2 0 0 0 0 0 0 
3 0 0 0 0 0 1 
    1 1 0 0 0 0 
    2 0 0 1 0 0 
4 0 0 0 0 0 1 

# do this groupby on outer index level [0,1,2,3,4] and reduce any inner group from multiple rows to one row 
df_dummies = pd.get_dummies(ser).groupby(level=0).apply(lambda group: group.max()) 

Out[64]: 
    a b c d e 
0 1 1 1 0 0 
1 1 0 0 1 0 
2 0 0 0 0 0 
3 1 0 1 0 1 
4 0 0 0 0 1 


df_dummies['sth_else'] = df.sth_else 

Out[67]: 
    a b c d e sth_else 
0 1 1 1 0 0  ba 
1 1 0 0 1 0  be 
2 0 0 0 0 0  bi 
3 1 0 1 0 1  bo 
4 0 0 0 0 1  bu 
+0

這是*快*!看起來相當不錯,謝謝!只是一個小方面的說明:你輸入熊貓爲NP - 我想應該是PD;) – dmeu

+0

@dmeu感謝您指出令人尷尬的錯字。 :-) –

+0

沒尷尬,只是有點混亂。我必須說這是一個非常酷的解決方案。但也許有一些評論解釋了兩條線將會是什麼樣的。我仍然在破譯=) – dmeu

1

這與今天的另一個問題非常相似。正如我在那個問題中所說的那樣,可能會有一個簡單而優雅的熊貓的方式來做到這一點,但我也發現,簡單地創建一個新的數據框並通過以下方式遍歷原始數據框來填充它很方便:

#import and create your data 
import pandas as pd 
DF = pd.DataFrame({ 'vals' : ['a,b,c', 'a,d', '', 'e,a,c', 'e'], 
        'other' : ['ba', 'be', 'bi', 'bo', 'bu'] 
        }, dtype = str) 

現在創建與other列新的數據幀形成DF爲索引和列是從DFval列中找到獨特的字符畫:

New_DF = pd.DataFrame({col : 0 for col in 
          set([letter for letter in ''.join([char for char in DF.vals.values]) 
          if letter.isalpha()])}, 
          index = DF.other) 

In [51]: New_DF 
Out[51]: 
     a b c d e 
other    
ba  0 0 0 0 0 
be  0 0 0 0 0 
bi  0 0 0 0 0 
bo  0 0 0 0 0 
bu  0 0 0 0 0 

現在只是在指數迭代的New_DF切片原始DF在該值和遍歷列,看看他們是否出現在relevant_string

for ind in New_DF.index: 
    relevant_string = str(DF[DF.other == ind].vals.values) 
    for col in list(New_DF.columns): 
     if col in relevant_string: 
      New_DF.loc[ind, col] += 1 

輸出看起來像這樣

In [54]: New_DF 
Out[54]: 
     a b c d e 
other    
ba  1 1 1 0 0 
be  1 0 0 1 0 
bi  0 0 0 0 0 
bo  1 0 1 0 1 
bu  0 0 0 0 1 
+0

嘿,也 - 非常快!感謝您的提議 – dmeu

+0

順便說一句@Woody Pride,這是另一個類似的問題,所以我可以看看? – dmeu

+0

http://stackoverflow.com/questions/31307014/new-pandas-dataframe-from-meta-information-of-existing-df/31309738#31309738 –