Как эффективно пометить каждое значение в ячейке после того, как я создал ячейки функцией pandas .cut ()? - PullRequest
2 голосов
/ 28 февраля 2020

Скажем, у меня есть столбец в кадре данных, который является 'user_age', и я создал 'user_age_bin' чем-то вроде:

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

Затем я строю модель машинного обучения с использованием 'user_age_bin' feature.

Затем я получил одну запись, которую мне нужно добавить в мою модель и сделать прогноз. Я не хочу использовать 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)

Но это решение совсем не элегантно. Я думаю, что должен быть лучший способ, верно?

Редактировать: я изменил код и добавил явный диапазон бина. Edit2: отредактированная формулировка и, надеюсь, теперь вопрос стал яснее.

Ответы [ 4 ]

1 голос
/ 28 февраля 2020

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
1 голос
/ 28 февраля 2020

Немного безобразный подход с двойным пониманием списка, но, похоже, он справляется с работой.

Настройка:

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)

Out:

   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
0 голосов
/ 28 февраля 2020

Вы не можете помещать строки в модель, поэтому вам нужно создать отображение и отслеживать его или создать отдельный столбец для использования позже

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

Сохраните копию данных с обоими полями, включенными до того, как они попадут в модель. Таким образом, вы можете сопоставить прогнозы со строковой версией полей бина, если хотите отображать их вместо числовых бинов.

0 голосов
/ 28 февраля 2020

Вы можете попробовать что-то вроде:

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-строку форматом, подобным синтаксису.

...