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

Posted

技术标签:

【中文标题】用 D 列中的值有条件地替换 A、B、C 列中的值【英文标题】:Conditional replacement of values in column A, B, C with value in column D 【发布时间】:2016-08-14 17:10:51 【问题描述】:

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

¦ 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 ¦

我的策略是:

    将 A、B、C 中的“x”值替换为 D 中的值 使用 pandas 的前向填充 将 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)

【问题讨论】:

你能提供你的意见df吗? 好的,谢谢你的建议 例如,数据框第 6 行的结果是什么? x.c.2 - 示例的第 4 行和第 8 行中显示的逻辑。 (很抱歉提供不同的示例和测试数据) 【参考方案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,它可以让我们区分应该为空的单元格和应该填充的单元格。

【讨论】:

我会把你的答案分解到最小的细节,并以我所学的为生。 :jawdropped: 感谢您对以下解决方案的反馈:***.com/a/37009971/1486768【参考方案2】:

您可以使用 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']

【讨论】:

我在处理数据时试图避免重复和 for 循环。然而,这仍然可以成为赢家。谢谢。【参考方案3】:

这是一种方法:

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

【讨论】:

我做到了这一点(以一种不太复杂的方式,使用df.fillna()),但我们的解决方案存在一个主要问题。 6 应该是 x.d9 应该是 y,等等 - 我们填充那些不应该填充的 NA。我认为在fillna() 之前必须有一个步骤,在parent_n &lt;&gt; parent_n-1 的每个单元格中用''s 替换NA。 我开始觉得awk更适合这个问题。【参考方案4】:

好吧,我终于通过@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

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

【讨论】:

以上是关于用 D 列中的值有条件地替换 A、B、C 列中的值的主要内容,如果未能解决你的问题,请参考以下文章

使用 dplyr 有条件地替换列中的值

tidyverse 和 dplyr:根据其他列有条件地替换列中的值

用另一列中的值替换缺失值

我需要用一些组替换我的 DataFrame 中的列中的值

将列中的 NA 替换为相邻列中的值

使用python替换电源查询列中的值? [关闭]