【问题标题】:Pandas efficiently cut column with bins argument based on another columnPandas 基于另一列有效地切割带有 bins 参数的列
【发布时间】:2019-12-10 09:09:04
【问题描述】:

我有以下熊猫数据框:

import numpy as np
import pandas as pd

np.random.seed(0)
test_df = pd.DataFrame({"category": ["A", "B"]*5, "value": np.random.uniform(size=10)})

print(test_df)
#  category     value
#0        A  0.548814
#1        B  0.715189
#2        A  0.602763
#3        B  0.544883
#4        A  0.423655
#5        B  0.645894
#6        A  0.437587
#7        B  0.891773
#8        A  0.963663
#9        B  0.383442

我想使用pandas.cutvalue 列进行分箱,但bins 参数需要根据category 列而有所不同。

具体来说,我想使用以下字典来定义用于cut 的垃圾箱:

bins = {
    "A": [0.00, 0.25, 0.50, 0.75, 1],
    #     0,    1,    2,    3,    4   <-- corresponding bin value
    "B": [0.00, 0.33, 0.66, 1]
    #     0,    1,    2,    3         <-- corresponding bin value
}

我想出了以下解决方案,即首先使用所有 bin 剪切 value 列:

cuts = {
    c: pd.cut(test_df["value"], bins=bins[c], labels=range(1, len(bins[c]))) for c in bins
}

然后使用numpy.select将适当的bin分配回test_df

test_df["bin"] = np.select(*zip(*[(test_df["category"] == c, cuts[c]) for c in bins]))
print(test_df)
#  category     value  bin
#0        A  0.548814    3
#1        B  0.715189    3
#2        A  0.602763    3
#3        B  0.544883    2
#4        A  0.423655    2
#5        B  0.645894    2
#6        A  0.437587    2
#7        B  0.891773    3
#8        A  0.963663    4
#9        B  0.383442    2

这是正确的答案,但有没有更有效的方法?理想情况下,应该有一种不涉及在每个不同的垃圾箱上调用cut 的方法。在我的真实数据中,我有超过 2 个 bin。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    也许将 numpy 与 np.searchsorted 一起使用:

    test_df['bin'] = [np.searchsorted(bins[i], v) for i, v in test_df.values]
    

    输出:

      category     value  bin
    0        A  0.548814    3
    1        B  0.715189    3
    2        A  0.602763    3
    3        B  0.544883    2
    4        A  0.423655    2
    5        B  0.645894    2
    6        A  0.437587    2
    7        B  0.891773    3
    8        A  0.963663    4
    9        B  0.383442    2
    

    时间

    %timeit np.select(zip([(test_df["category"] == c, cut[c]) for c in 垃圾箱]))
    每个循环 1.21 毫秒 ± 14.3 微秒(平均值 ± 标准偏差。7 次运行,每次 1000 次循环)

    %timeit [np.searchsorted(bins[i], v) for i, v in test_df.values]
    每个循环 301 µs ± 4.14 µs(7 次运行的平均值 ± 标准偏差,每次 1000 个循环)

    【讨论】:

    • 有趣,但这在我的情况下有效,因为我使用的是基于 range 的标签,对吗?
    • 我认为我们使用 np.searchsorted 返回的索引来获取您想要的标签。如果我正确理解您的问题。
    • 我的方法的时间实际上更糟,因为你不包括cuts的创建
    【解决方案2】:

    另一个解决问题的方法是使用groupby

    def applied(x):
        _bins = bins[x.category.iat[0]]
        return pd.cut(x.value, bins=_bins, labels=range(1,len(_bins)))
    
    test_df['bin']= test_df.groupby('category').apply(applied).reset_index(level= 0, drop= True)
    

    但与@Scott Boston 的相比,它实际上相当慢

    【讨论】:

      猜你喜欢
      • 2019-08-10
      • 1970-01-01
      • 2018-11-22
      • 2021-07-14
      • 1970-01-01
      • 1970-01-01
      • 2017-02-15
      • 2013-07-01
      • 1970-01-01
      相关资源
      最近更新 更多