【问题标题】:Is there a faster way to update dataframe column values based on conditions?是否有更快的方法来根据条件更新数据框列值?
【发布时间】:2023-04-07 05:56:01
【问题描述】:

我正在尝试处理数据框。这包括创建新列并根据其他列中的值更新它们的值。更具体地说,我有一个要分类的预定义“来源”。该来源可以属于三个不同的类别“source_dtp”、“source_dtot”和“source_cash”。我想根据原始“源”列向数据框中添加三个新列,这些列由 1 或 0 组成。

我目前能够做到这一点,只是真的很慢...

原始列示例:

source
_id                     
AV4MdG6Ihowv-SKBN_nB    DTP
AV4Mc2vNhowv-SKBN_Rn    Cash 1
AV4MeisikOpWpLdepWy6    DTP
AV4MeRh6howv-SKBOBOn    Cash 1
AV4Mezwchowv-SKBOB_S    DTOT
AV4MeB7yhowv-SKBOA5b    DTP

期望的输出:

source_dtp  source_dtot source_cash
_id         
AV4MdG6Ihowv-SKBN_nB    1.0 0.0 0.0
AV4Mc2vNhowv-SKBN_Rn    0.0 0.0 1.0
AV4MeisikOpWpLdepWy6    1.0 0.0 0.0
AV4MeRh6howv-SKBOBOn    0.0 0.0 1.0
AV4Mezwchowv-SKBOB_S    0.0 1.0 0.0
AV4MeB7yhowv-SKBOA5b    1.0 0.0 0.0

这是我目前的方法,但速度很慢。我更喜欢这样做的矢量化形式,但我不知道如何 - 因为条件非常复杂。

# For 'source' we will use the following classes:
source_cats = ['source_dtp', 'source_dtot', 'source_cash']
# [0, 0, 0] would imply 'other', hence no need for a fourth category

# add new features to dataframe, initializing to nan
for cat in source_cats:
    data[cat] = np.nan

for row in data.itertuples():
    # create series to hold the result per row e.g. [1, 0, 0] for `cash`
    cat = [0, 0, 0]
    index = row[0]
    # to string as some entries are numerical
    source_type = str(data.loc[index, 'source']).lower()
    if 'dtp' in source_type:
        cat[0] = 1
    if 'dtot' in source_type:
        cat[1] = 1
    if 'cash' in source_type:
        cat[2] = 1
    data.loc[index, source_cats] = cat

我正在使用 itertuples(),因为它被证明比 interrows() 更快。

有没有一种更快的方法来实现与上述相同的功能?

编辑:这不仅仅是关于创建一个热编码。它归结为根据另一列的值更新列值。例如。如果我有某个location_id,我想更新其各自的longitudelatitude 列-基于该原始ID(不以我上面的方式进行迭代,因为它对于大型数据集来说真的很慢)。

【问题讨论】:

  • df.source.str.get_dummies() 将为您提供 0 和 1。然后,使用pd.concat 加入您的数据框,或者直接调用df.assign
  • 是的,您可以使用 np.where 或 np.select 执行此操作。如果您提出了一个具有良好样本数据和预期结果的更好问题,我们 Stack Overflow 社区将向您展示如何做。
  • 正如@ScottBoston 所说,从一开始就提到这一点会很有帮助。您编写所有这些代码来演示 MCVE 的事实使我们相信它反映了您的实际用例。现在,您需要提供更多数据和预期输出,以便我们了解您的实际用例以及它与这个用例有何不同。
  • @vconvo 如果您的问题得到了here 的回答,请也关闭此问题,并接受我们的答案之一。
  • @coldspeed @Scott Boston 我会提出另一个问题——我没想到会有针对上述问题的特定解决方案 (get_dummies())

标签: python pandas dataframe data-processing


【解决方案1】:

另一种方法是在数据帧上使用pd.get_dummies。首先将'_id'放入索引中。

source = source.set_index('_id')
df_out = pd.get_dummies(source).reset_index()

print(df_out)

输出:

                    _id  source_Cash 1  source_DTOT  source_DTP
0  AV4MdG6Ihowv-SKBN_nB              0            0           1
1  AV4Mc2vNhowv-SKBN_Rn              1            0           0
2  AV4MeisikOpWpLdepWy6              0            0           1
3  AV4MeRh6howv-SKBOBOn              1            0           0
4  AV4Mezwchowv-SKBOB_S              0            1           0
5  AV4MeB7yhowv-SKBOA5b              0            0           1

【讨论】:

    【解决方案2】:

    您可以使用str.get_dummies 获取您的 OHEncoding。

    c = df.source.str.get_dummies().add_prefix('source_').iloc[:, ::-1]
    c.columns = c.columns.str.lower().str.split().str[0]
    print(c)
       source_dtp  source_dtot  source_cash
    0           1            0            0
    1           0            0            1
    2           1            0            0
    3           0            0            1
    4           0            1            0
    5           1            0            0
    

    接下来,使用pd.concatc_id 连接起来。

    df = pd.concat([df._id, c], 1)
    print(df)
                        _id  source_dtp  source_dtot  source_cash
    0  AV4MdG6Ihowv-SKBN_nB           1            0            0
    1  AV4Mc2vNhowv-SKBN_Rn           0            0            1
    2  AV4MeisikOpWpLdepWy6           1            0            0
    3  AV4MeRh6howv-SKBOBOn           0            0            1
    4  AV4Mezwchowv-SKBOB_S           0            1            0
    5  AV4MeB7yhowv-SKBOA5b           1            0            0
    

    改进!现在稍微流畅了,感谢 Scott Boston 的 set_index - reset_index 范例:

    df = df.set_index('_id')\
          .source.str.get_dummies().iloc[:, ::-1]
    df.columns = df.columns.str.lower().str.split().str[0]
    df = df.add_prefix('source_').reset_index()
    
    print(df)
                        _id  source_dtp  source_dtot  source_cash
    0  AV4MdG6Ihowv-SKBN_nB           1            0            0
    1  AV4Mc2vNhowv-SKBN_Rn           0            0            1
    2  AV4MeisikOpWpLdepWy6           1            0            0
    3  AV4MeRh6howv-SKBOBOn           0            0            1
    4  AV4Mezwchowv-SKBOB_S           0            1            0
    5  AV4MeB7yhowv-SKBOA5b           1            0            0
    

    【讨论】:

    • 有趣...我无法完全理解条件的评估方式 - 1 和 0 是如何分配的?在更复杂的条件下如何做到这一点?
    • @vcovo 我不太确定你在问什么,但pd.get_dummies 只是分配了一个热编码,这正是你想要的。对于更“复杂的条件”,您需要指定这些条件是什么,以便可以适当地解决它们。
    • 让我改写一下:如果source="Cash 123",data.source.str.get_dummies() 怎么知道为[source_dtp, source_dtot, source_cash] 分配[0, 0, 1] if 'cash' in source_type 条件在哪里发挥作用?关于“更复杂的条件”:例如if any(st in source_type for st in cash_types) 其中cash_types 是一个数组,例如['cash', 'money', 'contant']
    • @vcovo 简单地说,get_dummies 会找到所有唯一的项目,并按顺序为它们分配位置。然后,对于每个条目,根据该行的值设置相应的位。对于更复杂的情况,pd.get_dummies 在这里不起作用。这是一个仅适用于此用例的特殊功能。对于更复杂的情况,需要更多涉及的方法。假设这是您的实际用例,您可能希望使用反映这一点的 MVCE 打开一个新问题!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-01
    • 2020-06-25
    • 2019-10-14
    • 2023-02-21
    • 2021-01-14
    • 2020-12-01
    • 2021-03-01
    相关资源
    最近更新 更多