有条件地用A列中的值替换A,B,C列中的值

时间:2016-04-21 13:09:26

标签: python pandas dataframe normalization hierarchy

我正在清理一个凌乱的数据源,描述一个如下所示的层次结构。我正在使用Python和pandas。

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

我想生成唯一的ID,这些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. 将A,B,C中的'x'值替换为D
  2. 中的值
  3. 使用pandas'forward na fill
  4. 将A,B和C连接到列ID
  5. 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)
    

4 个答案:

答案 0 :(得分: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']

答案 1 :(得分: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

答案 2 :(得分: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,它可以让我们将应该为空的单元格与应该填充的单元格区分开来。

答案 3 :(得分: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

非常感谢任何评论/推荐。