【问题标题】:Pandas - conditionally select source column of data for a new column based on row valuePandas - 根据行值有条件地为新列选择源数据列
【发布时间】:2020-07-13 21:37:06
【问题描述】:

是否有允许根据条件从不同列中进行选择的 pandas 函数?这类似于 SQL Select 子句中的 CASE 语句。例如,假设我有以下 DataFrame:

foo = DataFrame(
    [['USA',1,2],
    ['Canada',3,4],
    ['Canada',5,6]], 
    columns = ('Country', 'x', 'y')
)

我想在 Country=='USA' 时从“x”列中选择,当 Country=='Canada' 时从“y”列中选择,结果如下:

  Country  x  y  z
0     USA  1  2  1
1  Canada  3  4  4
2  Canada  5  6  6

[3 rows x 4 columns]

【问题讨论】:

  • z从何而来?
  • z 将是“组合”列,来自 x 或 y 列,具体取决于国家/地区

标签: python pandas


【解决方案1】:

使用DataFrame.whereother 参数和pandas.concat

>>> import pandas as pd
>>>
>>> foo = pd.DataFrame([
...     ['USA',1,2],
...     ['Canada',3,4],
...     ['Canada',5,6]
... ], columns=('Country', 'x', 'y'))
>>>
>>> z = foo['x'].where(foo['Country'] == 'USA', foo['y'])
>>> pd.concat([foo['Country'], z], axis=1)
  Country  x
0     USA  1
1  Canada  4
2  Canada  6

如果要将z 作为列名,请指定keys

>>> pd.concat([foo['Country'], z], keys=['Country', 'z'], axis=1)
  Country  z
0     USA  1
1  Canada  4
2  Canada  6

【讨论】:

  • 我将我的答案从@EdChum 的答案改为这个,因为它更易于阅读且性能更高。
【解决方案2】:

这可行:

In [84]:

def func(x):
    if x['Country'] == 'USA':
        return x['x']
    if x['Country'] == 'Canada':
        return x['y']
    return NaN
foo['z'] = foo.apply(func(row), axis = 1)
foo
Out[84]:
  Country  x  y  z
0     USA  1  2  1
1  Canada  3  4  4
2  Canada  5  6  6

[3 rows x 4 columns]

你可以使用loc:

In [137]:

foo.loc[foo['Country']=='Canada','z'] = foo['y']
foo.loc[foo['Country']=='USA','z'] = foo['x']
foo
Out[137]:
  Country  x  y  z
0     USA  1  2  1
1  Canada  3  4  4
2  Canada  5  6  6

[3 rows x 4 columns]

编辑

虽然使用 loc 很笨拙,但使用较大的数据帧时会更好地扩展,因为此处的应用会针对每一行调用,而使用布尔索引将被矢量化。

【讨论】:

  • 是的,我写了一个类似的函数。我想知道 pandas 中是否有类似这样工作的东西,而不必编写函数,因为这是我经常做的事情。
  • 另一种方法是使用布尔索引查看我的答案
  • 我不认为使用 loc 有效,因为布尔掩码总是产生 DataFrame 的副本,而不是视图。
  • 只有当该列已经存在时才是真的与需要 60.2 毫秒的 apply 方法相比
  • 如果你只做 1 或另一个,这也可以:foo['z'] = foo['y'].where(foo['Country']=='Canada',foo['x'])
【解决方案3】:

这是一个通用解决方案,可以在给定另一列中的值的情况下选择任意列。

这具有将查找逻辑分离到一个简单的dict 结构中的额外好处,便于修改。

import pandas as pd
df = pd.DataFrame(
    [['UK', 'burgers', 4, 5, 6],
    ['USA', 4, 7, 9, 'make'],
    ['Canada', 6, 4, 6, 'you'],
    ['France', 3, 6, 'fat', 8]],
    columns = ('Country', 'a', 'b', 'c', 'd')
)

我扩展到将条件结果存储在外部查找结构中的操作 (dict)

lookup = {'Canada': 'd', 'France': 'c', 'UK': 'a', 'USA': 'd'}

为存储在dict 中的每一列循环pd.DataFrame,并使用条件表中的值来确定选择哪一列

for k,v in lookup.iteritems():
    filt = df['Country'] == k
    df.loc[filt, 'result'] = df.loc[filt, v] # modifies in place

上一堂人生课

In [69]: df
Out[69]:
  Country        a  b    c     d   result
0      UK  burgers  4    5     6  burgers
1     USA        4  7    9  make     make
2  Canada        6  4    6   you      you
3  France        3  6  fat     8      fat

【讨论】:

  • 这行得通,但我收到警告说A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead 知道如何解决它吗?
  • 我不会担心的。我们希望这种情况发生。您可以通过在该行添加“.copy()”来摆脱它,但这不是必需的
【解决方案4】:

numpy.select 非常适合这项工作,尤其是有多种选择时。它在数据帧中运行良好:

conditions = [
    foo['Country'] == 'USA', 
    foo['Country'] == 'Canada',
]

choices = [
    foo['x'],
    foo['y'],
]

foo['z'] = np.select(conditions, choices, default = pd.NA)

【讨论】:

  • 这是一个绝妙的解决方案!特别是对于习惯 SQL 'DECODE' 语法的人来说,因为它与它非常相似。谢谢!
【解决方案5】:

我的尝试:

temp1 = foo[(foo['Country'] == 'Canada')][['Country', 'y']].rename(columns={'y': 'z'})
temp2 = foo[(foo['Country'] == 'USA')][['Country', 'x']].rename(columns={'x': 'z'})
wanted_df = pd.concat([temp1, temp2])

【讨论】:

    猜你喜欢
    • 2016-11-02
    • 1970-01-01
    • 1970-01-01
    • 2018-06-10
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-12
    相关资源
    最近更新 更多