在我通过 pandas.cut() 函数创建 bin 后,如何有效地将每个值标记为 bin?

How to efficiently label each value to a bin after I created the bins by pandas.cut() function?

假设我在数据框中有一列 'user_age',并且我通过类似以下内容创建了 'user_age_bin':

df['user_age_bin']= pd.cut(df['user_age'], bins=[10, 15, 20, 25,30])

然后我使用 'user_age_bin' 功能构建机器学习模型。

接下来,我得到了一条记录,我需要将其放入我的模型中并进行预测。我不想按原样使用 user_age,因为模型使用 user_age_bin。那么,如何将 user_age 值(例如 28)转换为 user_age_bin?我知道我可以创建这样的函数:

def assign_bin(age):
    if age < 10:
        return '<10'
    elif age< 15:
        return '10-15'
     ... etc. etc.

然后做:

user_age_bin = assign_bin(28)

但是这个方案一点都不优雅。我想一定有更好的方法吧?

编辑:我更改了代码并添加了明确的 bin 范围。 Edit2:编辑了措辞,希望问题现在更清楚了。

您可以尝试类似的方法:

bins=[10, 15, 20, 25, 30]
labels = [f'<{bins[0]}', *(f'{a}-{b}' for a, b in zip(bins[:-1], bins[1:])), f'{bins[-1]}>']
pd.cut(df['user_age'], bins=bins, labels=labels)

请注意,如果您使用的是 python<3.7,则应将 f-string 替换为类似语法的格式。

双列表理解的有点丑陋的方法,但似乎可以完成工作。

设置:

import pandas as pd
import numpy as np
np.random.seed(42)

bins = [10, 15, 20, 25, 30, np.Inf]
labels = bins[1:]
ages = np.random.randint(10, 35, 10)
df = pd.DataFrame({"user_age": ages})
df["user_age_bin"] = pd.cut(df["user_age"], bins=bins, labels=labels)
print(df)

输出:

   user_age user_age_bin
0        16         20.0
1        29         30.0
2        24         25.0
3        20         20.0
4        17         20.0
5        30         30.0
6        16         20.0
7        28         30.0
8        32          inf
9        20         20.0

作业:

# `new_ages` is what you want to assign labels to, used `ages` for simplicity
new_ages = ages
ids = [np.argmax([age <= x for x in labels]) for age in new_ages]
assigned_labels = [labels[i] for i in ids]
print(pd.DataFrame({"new_ages": new_ages, "assigned_labels": assigned_labels, "user_age_bin": df["user_age_bin"]}))

输出:

   new_ages  assigned_labels user_age_bin
0        16             20.0         20.0
1        29             30.0         30.0
2        24             25.0         25.0
3        20             20.0         20.0
4        17             20.0         20.0
5        30             30.0         30.0
6        16             20.0         20.0
7        28             30.0         30.0
8        32              inf          inf
9        20             20.0         20.0

您不能将字符串放入模型中,因此您需要创建一个映射并跟踪它或创建一个单独的列供以后使用

def apply_age_bin_numeric(value):
    if value <= 10:
        return 1
    elif value > 10 and value <= 20:
        return 2
    elif value > 21 and value <= 30:
        return 3  
    etc....  

def apply_age_bin_string(value):
    if value <= 10:
        return '<=10'
    elif value > 10 and value <= 20:
        return '11-20'
    elif value > 21 and value <= 30:
        return '21-30' 
    etc....

df['user_age_bin_numeric']= df['user_age'].apply(apply_age_bin_numeric)
df['user_age_bin_string']= df['user_age'].apply(apply_age_bin_string)  

对于模型,您将保留 user_age_bin_numeric 并删除 user_age_bin_string

在进入模型之前,保存包含两个字段的数据副本。这样您就可以将预测匹配回 bin 字段的字符串版本,如果您想显示这些而不是数字 bin。

tl;dr:np.digitize 是一个很好的解决方案。

在阅读了此处的所有评论和答案并进行了更多谷歌搜索后,我认为我得到了一个令我非常满意的解决方案。谢谢大家!

设置

import pandas as pd
import numpy as np
np.random.seed(42)

bins = [0, 10, 15, 20, 25, 30, np.inf]
labels = bins[1:]
ages = list(range(5, 90, 5))
df = pd.DataFrame({"user_age": ages})
df["user_age_bin"] = pd.cut(df["user_age"], bins=bins, labels=False)

# sort by age 
print(df.sort_values('user_age'))

输出:

 user_age  user_age_bin
0          5             0
1         10             0
2         15             1
3         20             2
4         25             3
5         30             4
6         35             5
7         40             5
8         45             5
9         50             5
10        55             5
11        60             5
12        65             5
13        70             5
14        75             5
15        80             5
16        85             5

分配类别:

# a new age value
new_age=30

# use this right=True and '-1' trick to make the bins match
print(np.digitize(new_age, bins=bins, right=True) -1)

输出:

4