Собесов

Сценарий: np.where, маска и np.select — что выбрать

PythonNumpy и scipyЛёгкаяJunior

Условие

Нужно создать новую колонку по условиям. Когда использовать np.where, когда np.select, когда булеву маску с присваиванием?

Решение

Случай 1: одно условие if-else

df['bonus'] = np.where(df['vip'], df['amount']*0.2, df['amount']*0.05)

Случай 2: несколько условий

np.select — самый чистый способ:

conds = [
    df['amount'] >= 100_000,
    df['amount'] >= 10_000,
    df['amount'] >= 1_000,
]
choices = ['platinum', 'gold', 'silver']
df['tier'] = np.select(conds, choices, default='bronze')

Случай 3: модификация по маске in-place

mask = df['amount'] < 0
df.loc[mask, 'amount'] = 0       # «обнулить отрицательные»
df.loc[mask, 'is_refund'] = True # ещё и поставить флаг

Случай 4: nested-условия

Лучше избегать вложенных np.where — становится нечитаемо. Использовать np.select.

# Плохо:
df['tier'] = np.where(df['amount'] >= 100000, 'platinum',
                np.where(df['amount'] >= 10000, 'gold',
                    np.where(df['amount'] >= 1000, 'silver', 'bronze')))
 
# Хорошо: np.select как выше

Подводные камни

  1. np.where(cond, A, B) всегда вычисляет и A, и B — если один из них тяжёлый, считается зря. Маска ленивее.
  2. np.select берёт первое подходящее условие — порядок важен.
  3. Типы в choices могут смешиваться (['gold', 0]) → итоговый dtype object.
  4. Маска с присваиванием меняет df in-place — при цепочках копирования возможен SettingWithCopyWarning.
  5. На pandas-Series лучше Series.where(cond, other) или Series.mask(cond, other) — сохраняют индекс и dtype.

Эталонный ответ

Одно условие → np.where; много → np.select; модификация in-place → df.loc[mask, col] = value. Вложенные np.where заменяйте на np.select.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти