У меня есть:
df = pl.DataFrame({'key':['a','a','a','b','b','b'],'a':[2,4,6,1,2,3]})
print(df)
shape: (6, 2)
┌─────┬─────┐
│ key ┆ a │
│ --- ┆ --- │
│ str ┆ i64 │
╞═════╪═════╡
│ a ┆ 2 │
│ a ┆ 4 │
│ a ┆ 6 │
│ b ┆ 1 │
│ b ┆ 2 │
│ b ┆ 3 │
└─────┴─────┘
Я хочу добавить столбец с возрастающими целыми числами в каждой группе, определенной ключом
df = pl.DataFrame({'key':['a','a','a','b','b','b'],'a':[2,4,6,1,2,3], 'r': [1,2,3,1,2,3]})
print(df)
shape: (6, 3)
┌─────┬─────┬─────┐
│ key ┆ a ┆ r │
│ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 │
╞═════╪═════╪═════╡
│ a ┆ 2 ┆ 1 │
│ a ┆ 4 ┆ 2 │
│ a ┆ 6 ┆ 3 │
│ b ┆ 1 ┆ 1 │
│ b ┆ 2 ┆ 2 │
│ b ┆ 3 ┆ 3 │
└─────┴─────┴─────┘
Как мне это сделать?
Нашел ответ на проблему GitHub с полярами: используйте cum_count
df.with_columns(pl.col('c').cum_count().over("key").alias("r"))
Вы можете использовать int_range() + pl.len() в сочетании с .over()
df = pl.DataFrame({
"key":["a","a","a","a","b","b","b","b"],
"a":[2,4,6,8,1,None,None,4]
})
df.with_columns(r = pl.int_range(pl.len()).over("key") + 1)
shape: (8, 3)
┌─────┬──────┬─────┐
│ key ┆ a ┆ r │
│ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 │
╞═════╪══════╪═════╡
│ a ┆ 2 ┆ 1 │
│ a ┆ 4 ┆ 2 │
│ a ┆ 6 ┆ 3 │
│ a ┆ 8 ┆ 4 │
│ b ┆ 1 ┆ 1 │
│ b ┆ null ┆ 2 │
│ b ┆ null ┆ 3 │
│ b ┆ 4 ┆ 4 │
└─────┴──────┴─────┘
pl.int_range(pl.len())
эквивалентно pl.int_range(start=0, end=pl.len())
None
(по умолчанию), используется значение start
, а start
устанавливается на 0
.pl.len()
дает нам «количество строк в контексте».
>>> df.select(pl.len())
shape: (1, 1)
┌─────┐
│ len │
│ --- │
│ u32 │
╞═════╡
│ 8 │
└─────┘
В групповом контексте (т. е. .agg()
или .over()
) мы получаем количество строк в каждой группе.
>>> df.group_by("key").agg(pl.len())
shape: (2, 2)
┌─────┬─────┐
│ key ┆ len │
│ --- ┆ --- │
│ str ┆ u32 │
╞═════╪═════╡
│ b ┆ 4 │
│ a ┆ 4 │
└─────┴─────┘
.cum_count() недавно изменился способ обработки нулевых значений.
Он больше не гарантирует «уникальный счетчик», если вы используете его для данных, содержащих значения NULL.
>>> df.with_columns(r = pl.col("a").cum_count().over("key"))
shape: (8, 3)
┌─────┬──────┬─────┐
│ key ┆ a ┆ r │
│ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ u32 │
╞═════╪══════╪═════╡
│ a ┆ 2 ┆ 1 │
│ a ┆ 4 ┆ 2 │
│ a ┆ 6 ┆ 3 │
│ a ┆ 8 ┆ 4 │
│ b ┆ 1 ┆ 1 │
│ b ┆ null ┆ 1 │ # <-
│ b ┆ null ┆ 1 │ # <-
│ b ┆ 4 ┆ 2 │
└─────┴──────┴─────┘
arange
был переименован в int_range
- я расширил pl.len()
еще немного - это помогает?
Просто синтаксис pl.int_range(pl.len())
мне не понятен, так как длину вычислять нечего. Это просто pl.len()
без аргументов. Кроме того, согласно сигнатуре функции pl.int_range
не следует ли передавать pl.len()
в аргумент start
?
ох, понятно... если end
нет (то есть, если оно равно None), то значение start используется в качестве конца, а начало устанавливается на 0
df.with_columns(r = pl.arange(pl.col("a").len()).over("key") + 1)
Я могу интуитивно понять, что это делает, и могу расширить синтаксис на другие сценарии, не задумываясь об этом… использую поляры всего 4-5 дней. Надеюсь, со временем я это освою. Библиотека невероятно замечательная (это мягко сказано)
pl.len()
без аргументов дает длину текущего «контекста», например. «длина столбца» или «длина группы». Да, в конечном итоге это становится сокращенным синтаксисом для pl.int_range(start=0, end=pl.len())
Если вы сначала явно добавите индекс, вы можете избежать каких-либо проблем с нулевыми значениями с помощью вашего подхода cum_count: df.with_row_index("r").with_columns(pl.col("r").cum_count().over("key"))
но он немного «многословен».
Я хотел использовать
pl.arange
, но не мог понять, как динамически получить начальный и конечный аргументы. Ваш синтаксисpl.int_range(pl.len())
, не могли бы вы пролить свет на то, как он работает, или, может быть, указать мне правильное направление, чтобы прочитать об этом?