Рассмотрим таблицу поиска, подобную этой:
lower_bound upper_bound category
0 3 6 A
1 10 40 B
2 80 200 C
3 350 600 D
4 900 1500 E
Затем есть DataFrame элементов, которые нам нужно классифицировать на основе вышеуказанных условий:
id value
0 id_1 20
1 id_2 500
2 id_3 1000
Каждый из этих элементов будет классифицироваться в соответствии с их значением, которое должно быть между одним из указанных диапазонов, существующих в таблице поиска. Так:
id value category
0 id_1 20 B
1 id_2 500 D
2 id_3 1000 E
Я пробовал следующее, что работает:
conditions = []
choices = []
for condition in lookup_df.to_dict('records'):
conditions.append(
(df['value'].between(condition['lower_bound'], condition['upper_bound']))
)
choices.append(condition['category'])
А потом:
df['category'] = np.select(conditions, choices, default=np.nan)
Есть ли способ сгенерировать условия без преобразования в dict и зацикливания поиска DataFrame?






Вы можете использовать пустую трансляцию. Идея состоит в том, чтобы создать логическую маску, которая возвращает True для диапазона, в который попадает каждое «значение» в lookup_df. Затем выберите совпадающие значения, используя логическое индексирование.
vals = df['value'].to_numpy()
msk = (lookup_df[['lower_bound']].to_numpy() < vals) & (vals < lookup_df[['upper_bound']].to_numpy())
df['category'] = lookup_df[['category']].to_numpy().repeat(len(df), axis=1)[msk]
Выход:
id value category
0 id_1 20 B
1 id_2 500 D
2 id_3 1000 E
Если вам нужны значения NaN для значений, выходящих за пределы любого диапазона, здесь лучше всего подойдет dot product (это точно так же, как @Корралиен решение):
vals = df[['value']].to_numpy()
msk = (lookup_df['lower_bound'].to_numpy() < vals) & (vals < lookup_df['upper_bound'].to_numpy())
df = df.assign(category=msk.dot(lookup_df['category'])).replace('', float('nan'))
@ Этелом Это верно. В этом случае лучше всего подойдет np.dot.
Вы можете использовать pd.merge_asof:
output = pd.merge_asof(df, lookup_df[["lower_bound","category"]], left_on = "value", right_on = "lower_bound").drop("lower_bound", axis=1)
>>> output
id value category
0 id_1 20 B
1 id_2 500 D
2 id_3 1000 E
Вы можете использовать np.dot:
vals = np.vstack(df['value'].values)
lb = condition['lower_bound'].values <= vals
ub = vals <= condition['upper_bound'].values
df['category'] = np.dot(lb & ub, condition['category'])
Выход:
| я бы | ценность | категория |
|---|---|---|
| id_1 | 20 | Б |
| id_2 | 500 | Д |
| id_3 | 1000 | Е |
dot очень красиво; рад снова тебя видеть :-)
Мне очень нравится этот подход. Однако один вопрос: что произойдет, если элемент не может быть классифицирован (т. е. не находится между какими-либо границами)? Я думаю, что это было бы опущено вместо того, чтобы присвоить категорию nan или что-то в этом роде.