2016-04-21 90 views
1

我正在清理描述如下所示的層次結構的混亂數據源。我正在使用Python和熊貓。有條件地將列A,B,C中的值替換爲列D中的值

¦ A ¦ B ¦ C ¦ D ¦ 
----------------- 
¦ x ¦ ¦ ¦ a ¦ 
¦ ¦ x ¦ ¦ b ¦ 
¦ ¦ ¦ x ¦ c ¦ 
¦ ¦ ¦ x ¦ d ¦ 
¦ x ¦ ¦ ¦ e ¦ 
¦ ¦ x ¦ ¦ f ¦ 
¦ ¦ ¦ x ¦ g ¦ 
¦ ¦ ¦ x ¦ h ¦ 

我想生成唯一的ID也保持數據的層次性質。 (每父名稱是唯一的,不注重這部分請。)

¦ A ¦ B ¦ C ¦ D ¦ ID ¦ 
------------------------- 
¦ x ¦ ¦ ¦ a ¦ a  ¦ 
¦ ¦ x ¦ ¦ b ¦ a.b ¦ 
¦ ¦ ¦ x ¦ c ¦ a.b.c ¦ 
¦ ¦ ¦ x ¦ d ¦ a.b.d ¦ 
¦ x ¦ ¦ ¦ e ¦ e  ¦ <-- note, this is NOT e.b.d, 
¦ ¦ x ¦ ¦ f ¦ e.f ¦  so when parent changes 
¦ ¦ ¦ x ¦ g ¦ e.f.g ¦  fillna must not be applied 
¦ ¦ ¦ x ¦ h ¦ e.f.h ¦ 

我的策略是:

  1. 與值從d
  2. 取代A,B,C 'X' 值
  3. 使用大熊貓轉發NA填充
  4. 串連A,B和C進入塔ID

2和3是容易的,但我無法通過1.我可以代替X-ES與單個值:

df[df.loc[:,'A':'C'] == 'x'] = 1 

但如果我嘗試通過df.D而不是1不起作用。

請推薦一款優雅的pythonic解決方案。


源工作,並具:

import sys 
if sys.version_info[0] < 3: 
    from StringIO import StringIO 
else: 
    from io import StringIO 
import pandas as pd 

TESTDATA=StringIO(""" 
A;B;C;D;solution 
x;;;x;x 
;x;;a;xa 
;x;;b;xb 
;x;;c;xc 
;;x;1;xc1 
;;x;2;xc2 
;x;;d;xd 
;;x;3;xd3 
;;x;4;xd4 
x;;;y;y 
;x;;e;ye 
;;x;5;ye5 
;;x;6;ye6 
;x;;f;yf 
;;x;7;yf7 
;;x;8;yf8 
;;x;9;yf9""") 

df = pd.read_csv(TESTDATA, sep=";", header=False) 
+0

你可以給你輸入'df'嗎? –

+0

是的,謝謝你的建議 –

+0

例如,你的數據框的第6行的結果是什麼? –

回答

1

不是最漂亮的過,但像

w0 = df.iloc[:,:3] 
wx = w0 == 'x' 
wempty = (wx.cumsum(axis=1) >= 1).shift(axis=1).fillna(False) 
wfilled = w0.where(~wx, df.D, axis=0).ffill() 
w = w0.where(wempty, wfilled, axis=1).fillna('') 
df["new_solution"] = w.apply('.'.join,axis=1).str.rstrip(".") 

給我

>>> df 
     A B C D solution new_solution 
0  x NaN NaN x  x   x 
1 NaN x NaN a  xa   x.a 
2 NaN x NaN b  xb   x.b 
3 NaN x NaN c  xc   x.c 
4 NaN NaN x 1  xc1  x.c.1 
5 NaN NaN x 2  xc2  x.c.2 
6 NaN x NaN d  xd   x.d 
7 NaN NaN x 3  xd3  x.d.3 
8 NaN NaN x 4  xd4  x.d.4 
9  x NaN NaN y  y   y 
10 NaN x NaN e  ye   y.e 
11 NaN NaN x 5  ye5  y.e.5 
12 NaN NaN x 6  ye6  y.e.6 
13 NaN x NaN f  yf   y.f 
14 NaN NaN x 7  yf7  y.f.7 
15 NaN NaN x 8  yf8  y.f.8 
16 NaN NaN x 9  yf9  y.f.9 

這裏的技巧是使用cumsum,這讓我們區分哪些應該是空的,從應填補了細胞的細胞。

+0

我會分解你最小細節的答案,並以我學到的東西爲生。 :jawdropped: –

+0

感謝您對以下解決方案的反饋意見:http://stackoverflow.com/a/37009971/1486768 –

1

您可以使用IX代替LOC的:

df.ix[df.ix[:,'A'] == 'x','A'] = df.ix[df.ix[:,'A'] == 'x','D'] 
df.ix[df.ix[:,'B'] == 'x','B'] = df.ix[df.ix[:,'B'] == 'x','D'] 
df.ix[df.ix[:,'C'] == 'x','C'] = df.ix[df.ix[:,'C'] == 'x','D'] 
+0

我試圖避免重複和循環時處理數據。但是,這仍然可以成爲贏家。謝謝。 –

1

這裏有一個辦法:

dt = pd.DataFrame([np.where(df[n]=='x', df['D'], df[n]) for n in ['A','B','C']]).T 

dt.ffill().fillna('').apply(lambda x: '.'.join(x), axis=1).str.replace('\.+$','') 

Out[213]: 
0   x 
1  x.a 
2  x.b 
3  x.c 
4  x.c.1 
5  x.c.2 
6  x.d.2 
7  x.d.3 
8  x.d.4 
9  y.d.4 
10 y.e.4 
11 y.e.5 
12 y.e.6 
13 y.f.6 
14 y.f.7 
15 y.f.8 
16 y.f.9 
dtype: object 
+0

我明白了這一點(用不太複雜的方式,使用'df.fillna()'),但是我們的解決方案存在一個主要問題。 '6'應該是'x.d','9'應該是'y',等等 - 我們填充那些不應該被填充的NA。我認爲在'fillna()'前面必須有一個步驟,它在parent_n <> parent_n-1'的每個單元格中用'''替換NAs。 –

+0

我開始認爲'awk'更適合這個問題。 –

0

那麼,我終於來到這個解決方案,使用@DSM的一些技巧。

它只有一個臨時變量,主要用布爾型掩碼來解決問題。

# bool mask for empty cells that have non-empty cell before them 
nofills = (df.iloc[:,:3] == 'x').cumsum(axis=1) & ((df.iloc[:,:3] == 'x') == False) > 0 

# fill these with empty strings 
df[nofills] = '' 

# replace 'x'es with values from column D, ffill up NaNs then concat together into a new column 
df['solution2'] = df.iloc[:,:3].where(df.iloc[:,:3] != 'x', df.D, axis=0).ffill().apply(''.join, axis=1) 

print df 

結果:

 A B C D solution solution2 
0  x   x  x   x 
1 NaN x  a  xa  xa 
2 NaN x  b  xb  xb 
3 NaN x  c  xc  xc 
4 NaN NaN x 1  xc1  xc1 
5 NaN NaN x 2  xc2  xc2 
6 NaN x  d  xd  xd 
7 NaN NaN x 3  xd3  xd3 
8 NaN NaN x 4  xd4  xd4 
9  x   y  y   y 
10 NaN x  e  ye  ye 
11 NaN NaN x 5  ye5  ye5 
12 NaN NaN x 6  ye6  ye6 
13 NaN x  f  yf  yf 
14 NaN NaN x 7  yf7  yf7 
15 NaN NaN x 8  yf8  yf8 
16 NaN NaN x 9  yf9  yf9 

任何評論/建議是高度讚賞。

相關問題