Например:
import polars as pl
dates = pl.DataFrame(pl.Series("date", ["2024,2", "2024,12"]))
dates = dates.with_columns(pl.col("date").str.to_date("%Y,%m"))
Это не работает из-за пропущенного дня. Python strptime
добавляет день 1:
from time import strptime
strptime("2024,2", "%Y,%m")
Есть ли способ сделать что-то подобное с polars
, кроме простого прикрепления к веревочкам в день?
import polars as pl
dates = pl.DataFrame(pl.Series("date", ["2024,2", "2024,12"]))
dates = dates.with_columns((pl.col("date") + ",1").str.to_date(r"%Y,%m,%d"))
Это нормальное решение, но оно выполняет ненужную работу. Итак, лучшее решение, которое я придумал, чтобы не выполнять повторяющуюся работу, это:
import polars as pl
dates = pl.DataFrame(pl.Series("date", ["2024,2", "2024,12"]))
dates = (
dates.with_columns(
pl.col("date").str.split(",").list.to_struct(fields=["year", "month"])
)
.unnest("date")
.with_columns(date=pl.date(pl.col("year"), pl.col("month"), 1))
)
Я думаю, что оба ваших обходных пути действительны, поскольку вашим данным потребуется дополнительная очистка/стандартизация (и это нормально). Вот еще одна альтернатива, которая использует регулярное выражение для заполнения месяцев нулями по мере необходимости:
import polars as pl
import re
def zero_pad(s):
return re.sub(r",(\d)$", r",0\1", s) # pad number that come after a comma if it is only 1 digit
dates = pl.DataFrame(pl.Series("date", map(zero_pad, ["2024,2", "2024,12"])))
dates = dates.with_columns(pl.col("date").str.to_date("%Y,%m"))
print(dates)
Переход от списка к структуре требует копирования данных, а поскольку строки хранятся как stringview, может быть дешевле просто объединить строки, как в вашем первом решении.
По крайней мере, вы можете пропустить структурирование и отмену вложенности, что также не нужно, используя .list.get()
. Вы можете соединить его с оператором-моржом, чтобы он оставался СУХИМ.
dates = dates.with_columns(
date=pl.date(
(date_split := pl.col("date").str.split(",")).list.get(0),
date_split.list.get(1),
1,
)
)
Альтернативно вы можете пропустить список, используя .str.split_exact
, который заранее возвращает структуру. Если вы это сделаете, вы можете пропустить unnest с помощью .struct[0]
и .struct[1]
, как в
dates.with_columns(
date=pl.date(
(datestruct := pl.col("date").str.split_exact(",", 1)).struct[0],
datestruct.struct[1],
1,
)
)
df=pl.DataFrame({'dates':pl.date_range(pl.date(1970,1,1), pl.date(2024,7,1), '1mo', eager=True)})
df=df.with_columns(pl.col('dates').dt.strftime("%Y,%m"))
%%timeit
df.with_columns((pl.col('dates')+",1").str.to_date(r"%Y,%m,%d"))
731 µs ± 6.81 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
%%timeit
dates.with_columns(
date=pl.date(
(date_split := pl.col("date").str.split(",")).list.get(0),
date_split.list.get(1),
1,
)
)
1.35 ms ± 6.68 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
%%timeit
dates.with_columns(
date=pl.date(
(datestruct := pl.col("date").str.split_exact(",", 1)).struct[0],
datestruct.struct[1],
1,
)
)
1.29 ms ± 30.1 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
Использование объединения строк намного быстрее, чем разделение строки и использование конструктора даты.
Проблема не в заполнении нулями. Проблема в том, что хроно (ящик даты и времени под капотом) настаивает на том, чтобы день был частью даты.