Я пытаюсь использовать Polars.Series.cut , чтобы воспроизвести поведение объединения данных pandas.cut.
MRE значений и точек останова:
scores = [1111, 65, 88, -1111, 92]
breaks = [0, 50, 60, 70, 80, 90, 100]
В pandas.cut интервал равен нулю, если значение находится за пределами определенных краев:
import pandas as pd
df = pd.DataFrame({'score': scores})
df['bin'] = pd.cut(df['score'], breaks)
# score bin
# 0 1111 NaN <- null in pandas
# 1 65 (60.0, 70.0]
# 2 88 (80.0, 90.0]
# 3 -1111 NaN <- null in pandas
# 4 92 (90.0, 100.0]
Но с Polars.Series.cut, кажется, мы вынуждены включить ячейки (-inf, ...] и (..., inf]:
import polars as pl
df = pl.DataFrame({'score': scores})
df = df.with_columns(bin=pl.col('score').cut(breaks))
# shape: (5, 2)
# ┌───────┬────────────┐
# │ score ┆ bin │
# │ --- ┆ --- │
# │ i64 ┆ cat │
# ╞═══════╪════════════╡
# │ 1111 ┆ (100, inf] │ <- not null in polars
# │ 65 ┆ (60, 70] │
# │ 88 ┆ (80, 90] │
# │ -1111 ┆ (-inf, 0] │ <- not null in polars
# │ 92 ┆ (90, 100] │
# └───────┴────────────┘
Как мы можем воспроизвести контейнеры pandas.cut в Polars?





А как насчет использования is_between в качестве фильтра?
df.with_columns(bin=pl.when(pl.col('score').is_between(breaks[0], breaks[-1], closed='right'))
.then(pl.col('score').cut(breaks))
)
Или, что еще лучше, чтобы избежать повторного расчета категории, используйте предварительный фильтр:
df.with_columns(bin=pl.when(pl.col('score').is_between(breaks[0], breaks[-1], closed='right'))
.then(pl.col('score')).cut(breaks)
)
Выход:
┌───────┬───────────┐
│ score ┆ bin │
│ --- ┆ --- │
│ i64 ┆ cat │
╞═══════╪═══════════╡
│ 1111 ┆ null │
│ 65 ┆ (60, 70] │
│ 88 ┆ (80, 90] │
│ -1111 ┆ null │
│ 92 ┆ (90, 100] │
└───────┴───────────┘
Для полноты картины: если у вас уже есть DataFrame с категориальными данными, вы также можете сравнить с str, чтобы установить значения None:
df.with_columns(
pl.when(
~pl.col('bin').cast(pl.Utf8).str.contains('inf')
).then(pl.col('bin'))
)
┌───────┬───────────┐
│ score ┆ bin │
│ --- ┆ --- │
│ i64 ┆ cat │
╞═══════╪═══════════╡
│ 1111 ┆ null │
│ 65 ┆ (60, 70] │
│ 88 ┆ (80, 90] │
│ -1111 ┆ null │
│ 92 ┆ (90, 100] │
└───────┴───────────┘
Или вы можете исключить категории first() и last():
df.with_columns(
pl.when(
pl.col('bin') != pl.col('bin').cat.get_categories().first(),
pl.col('bin') != pl.col('bin').cat.get_categories().last(),
).then(pl.col('bin'))
)
┌───────┬───────────┐
│ score ┆ bin │
│ --- ┆ --- │
│ i64 ┆ cat │
╞═══════╪═══════════╡
│ 1111 ┆ null │
│ 65 ┆ (60, 70] │
│ 88 ┆ (80, 90] │
│ -1111 ┆ null │
│ 92 ┆ (90, 100] │
└───────┴───────────┘
Приятно знать, спасибо. А еще я немного удивлен, что bin= здесь не нужен. Я думаю, поляры просто выводят bin= из then()?
Только что заметил, что мы должны использовать
in_between(..., closed='right')для соответствия интерваламcut()(я небрежно работал с MRE и не включил самые крайние значения)