将多列的不同值组合成 Polars 中的一列
Combine different values of multiple columns into one column in Polars
我有以下 Python 代码 pandas
df['EVENT_DATE'] = df.apply(
lambda row: datetime.date(year=row.iyear, month=row.imonth, day=row.iday).strftime("%Y-%m-%d"), axis=1)
并希望将其转换为有效的 Polars 代码。有人知道解决这个问题吗?
Polars apply
将 return 行数据作为 tuple,因此您需要改用数字索引。示例:
import datetime
import polars as pl
df = pl.DataFrame({"iyear": [2020, 2021],
"imonth": [1, 2],
"iday": [3, 4]})
df['EVENT_DATE'] = df.apply(
lambda row: datetime.date(year=row[0], month=row[1], day=row[2]).strftime("%Y-%m-%d"))
如果 df
包含更多列或顺序不同,您可以在 df[["iyear", "imonth", "iday"]]
而不是 df
上使用 apply 来确保索引引用正确的元素。
可能有更好的方法来实现这一点,但这最接近 Pandas 代码。
另外,我的猜测是您不想将日期存储为字符串,而是存储为适当的 pl.Date
。您可以这样修改代码:
def days_since_epoch(dt):
return (dt - datetime.date(1970, 1, 1)).days
df['EVENT_DATE_dt'] = df.apply(
lambda row: days_since_epoch(datetime.date(year=row[0], month=row[1], day=row[2])), return_dtype=pl.Date)
我们首先将 Python date
转换为自 1970 年 1 月 1 日以来的天数,然后使用 apply
的 return_dtype
转换为 pl.Date
] 争论。转换为 pl.Date
需要一个 int 而不是 Python 日期时间,因为它最终将数据存储为 int。只需访问日期就可以很容易地看到这一点:
print(type(df["EVENT_DATE_dt"][0])) # >>> <class 'int'>
print(type(df["EVENT_DATE_dt"].dt[0])) # >>> <class 'datetime.date'>
如果演员表直接对 Python 日期时间进行操作,那就太好了。
编辑:关于性能与 Pandas 的对话。
对于 Pandas 和 Polars,如果您有许多重复行(对于 year/month/day),您可以通过使用缓存来加速应用来进一步加速。即
from functools import lru_cache
@lru_cache
def row_to_date(row):
return days_since_epoch(datetime.date(year=row[0], month=row[1], day=row[2]))
df['EVENT_DATE_dt'] = df.apply(row_to_date, return_dtype=pl.Date)
当有很多重复条目时,这将提高运行时间,但会占用一些内存。如果没有重复,它可能会减慢你的速度。
我也会回答你的一般问题,而不仅仅是你的具体用例。
对于您的具体情况,从 polars version >= 0.10.18
开始,推荐的创建所需内容的方法是使用 pl.date
或 pl.datetime
表达式。
鉴于此数据框,pl.date
用于按要求格式化日期。
import polars as pl
df = pl.DataFrame({
"iyear": [2001, 2001],
"imonth": [1, 2],
"iday": [1, 1]
})
df.with_columns([
pl.date("iyear", "imonth", "iday").dt.strftime("%Y-%m-%d").alias("fmt")
])
这输出:
shape: (2, 4)
┌───────┬────────┬──────┬────────────┐
│ iyear ┆ imonth ┆ iday ┆ fmt │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ str │
╞═══════╪════════╪══════╪════════════╡
│ 2001 ┆ 1 ┆ 1 ┆ 2001-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2001 ┆ 2 ┆ 1 ┆ 2001-02-01 │
└───────┴────────┴──────┴────────────┘
在单个表达式中收集其他列的其他方法
下面是对主要问题的更通用的回答。我们可以使用 map
来获取多个列,如 Series
,或者如果我们知道我们想要格式化一个字符串列,我们可以使用 pl.format
。 map
提供了最多的实用性。
df.with_columns([
# string fmt over multiple expressions
pl.format("{}-{}-{}", "iyear", "imonth", "iday").alias("date"),
# columnar lambda over multiple expressions
pl.map(["iyear", "imonth", "iday"], lambda s: s[0] + "-" + s[1] + "-" + s[2]).alias("date2"),
])
这输出
shape: (2, 5)
┌───────┬────────┬──────┬──────────┬──────────┐
│ iyear ┆ imonth ┆ iday ┆ date ┆ date2 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ str ┆ str │
╞═══════╪════════╪══════╪══════════╪══════════╡
│ 2001 ┆ 1 ┆ 1 ┆ 2001-1-1 ┆ 2001-1-1 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 2001 ┆ 2 ┆ 1 ┆ 2001-2-1 ┆ 2001-2-1 │
└───────┴────────┴──────┴──────────┴──────────┘
避免按行操作
不过,接受的答案在结果中是正确的。这不是对极坐标中的多列应用操作的推荐方法。访问行非常慢。导致大量缓存未命中,需要 运行 减慢 python 字节码并终止所有并行化/查询优化。
备注
在这种特定情况下,不推荐创建字符串数据的地图:
pl.map(["iyear", "imonth", "iday"], lambda s: s[0] + "-" + s[1] + "-" + s[2]).alias("date2"),
。由于内存的布局方式以及我们为每个字符串操作创建一个新列,这实际上非常昂贵(仅限字符串数据)。因此有 pl.format
和 pl.concat_str
.
我有以下 Python 代码 pandas
df['EVENT_DATE'] = df.apply(
lambda row: datetime.date(year=row.iyear, month=row.imonth, day=row.iday).strftime("%Y-%m-%d"), axis=1)
并希望将其转换为有效的 Polars 代码。有人知道解决这个问题吗?
Polars apply
将 return 行数据作为 tuple,因此您需要改用数字索引。示例:
import datetime
import polars as pl
df = pl.DataFrame({"iyear": [2020, 2021],
"imonth": [1, 2],
"iday": [3, 4]})
df['EVENT_DATE'] = df.apply(
lambda row: datetime.date(year=row[0], month=row[1], day=row[2]).strftime("%Y-%m-%d"))
如果 df
包含更多列或顺序不同,您可以在 df[["iyear", "imonth", "iday"]]
而不是 df
上使用 apply 来确保索引引用正确的元素。
可能有更好的方法来实现这一点,但这最接近 Pandas 代码。
另外,我的猜测是您不想将日期存储为字符串,而是存储为适当的 pl.Date
。您可以这样修改代码:
def days_since_epoch(dt):
return (dt - datetime.date(1970, 1, 1)).days
df['EVENT_DATE_dt'] = df.apply(
lambda row: days_since_epoch(datetime.date(year=row[0], month=row[1], day=row[2])), return_dtype=pl.Date)
我们首先将 Python date
转换为自 1970 年 1 月 1 日以来的天数,然后使用 apply
的 return_dtype
转换为 pl.Date
] 争论。转换为 pl.Date
需要一个 int 而不是 Python 日期时间,因为它最终将数据存储为 int。只需访问日期就可以很容易地看到这一点:
print(type(df["EVENT_DATE_dt"][0])) # >>> <class 'int'>
print(type(df["EVENT_DATE_dt"].dt[0])) # >>> <class 'datetime.date'>
如果演员表直接对 Python 日期时间进行操作,那就太好了。
编辑:关于性能与 Pandas 的对话。 对于 Pandas 和 Polars,如果您有许多重复行(对于 year/month/day),您可以通过使用缓存来加速应用来进一步加速。即
from functools import lru_cache
@lru_cache
def row_to_date(row):
return days_since_epoch(datetime.date(year=row[0], month=row[1], day=row[2]))
df['EVENT_DATE_dt'] = df.apply(row_to_date, return_dtype=pl.Date)
当有很多重复条目时,这将提高运行时间,但会占用一些内存。如果没有重复,它可能会减慢你的速度。
我也会回答你的一般问题,而不仅仅是你的具体用例。
对于您的具体情况,从 polars version >= 0.10.18
开始,推荐的创建所需内容的方法是使用 pl.date
或 pl.datetime
表达式。
鉴于此数据框,pl.date
用于按要求格式化日期。
import polars as pl
df = pl.DataFrame({
"iyear": [2001, 2001],
"imonth": [1, 2],
"iday": [1, 1]
})
df.with_columns([
pl.date("iyear", "imonth", "iday").dt.strftime("%Y-%m-%d").alias("fmt")
])
这输出:
shape: (2, 4)
┌───────┬────────┬──────┬────────────┐
│ iyear ┆ imonth ┆ iday ┆ fmt │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ str │
╞═══════╪════════╪══════╪════════════╡
│ 2001 ┆ 1 ┆ 1 ┆ 2001-01-01 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2001 ┆ 2 ┆ 1 ┆ 2001-02-01 │
└───────┴────────┴──────┴────────────┘
在单个表达式中收集其他列的其他方法
下面是对主要问题的更通用的回答。我们可以使用 map
来获取多个列,如 Series
,或者如果我们知道我们想要格式化一个字符串列,我们可以使用 pl.format
。 map
提供了最多的实用性。
df.with_columns([
# string fmt over multiple expressions
pl.format("{}-{}-{}", "iyear", "imonth", "iday").alias("date"),
# columnar lambda over multiple expressions
pl.map(["iyear", "imonth", "iday"], lambda s: s[0] + "-" + s[1] + "-" + s[2]).alias("date2"),
])
这输出
shape: (2, 5)
┌───────┬────────┬──────┬──────────┬──────────┐
│ iyear ┆ imonth ┆ iday ┆ date ┆ date2 │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ str ┆ str │
╞═══════╪════════╪══════╪══════════╪══════════╡
│ 2001 ┆ 1 ┆ 1 ┆ 2001-1-1 ┆ 2001-1-1 │
├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
│ 2001 ┆ 2 ┆ 1 ┆ 2001-2-1 ┆ 2001-2-1 │
└───────┴────────┴──────┴──────────┴──────────┘
避免按行操作
不过,接受的答案在结果中是正确的。这不是对极坐标中的多列应用操作的推荐方法。访问行非常慢。导致大量缓存未命中,需要 运行 减慢 python 字节码并终止所有并行化/查询优化。
备注
在这种特定情况下,不推荐创建字符串数据的地图:
pl.map(["iyear", "imonth", "iday"], lambda s: s[0] + "-" + s[1] + "-" + s[2]).alias("date2"),
。由于内存的布局方式以及我们为每个字符串操作创建一个新列,这实际上非常昂贵(仅限字符串数据)。因此有 pl.format
和 pl.concat_str
.