基于另一列,Pandas可以高效地使用“bins”参数对列进行切割。

3

我有以下的pandas DataFrame:

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
}

我想到了以下解决方案,首先使用所有的箱子来切割value列:
cuts = {
    c: pd.cut(test_df["value"], bins=bins[c], labels=range(1, len(bins[c]))) for c in bins
}

然后使用numpy.select将适当的箱子分配回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

这是正确答案,但是否有更高效的方法?理想情况下,应该有一种方法,不需要对每个不同的bin都调用cut。在我的真实数据中,我有超过2个bin。
2个回答

3

也许可以使用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, cuts[c]) for c in bins]))
平均每次循环耗时1.21毫秒,标准差为14.3微秒(7次运行的平均值,每个循环1000次)

%timeit [np.searchsorted(bins[i], v) for i, v in test_df.values]
平均每次循环耗时301微秒,标准差为4.14微秒(7次运行的平均值,每个循环1000次)


有趣,但这在我的情况下有效,因为我正在使用基于“范围”的标签,对吧? - pault
1
我认为我们可以使用np.searchsorted返回的索引来获取您想要的标签。如果我正确理解了您的问题。 - Scott Boston
1
我的方法的计时实际上更糟,因为您没有包括“cuts”的创建。 - pault

0
另一种解决问题的方法是使用 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相比,它实际上相当慢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接