我有两个数据框,
df = pl.DataFrame({'url': ['https//abc.com', 'https//abcd.com', 'https//abcd.com/aaa', 'https//abc.com/abcd']})
conditions_df = pl.DataFrame({'url': ['https//abc.com', 'https//abcd.com', 'https//abcd.com/aaa', 'https//abc.com/aaa'], 'category': [['a'], ['b'], ['c'], ['d']]})
现在我想要一个 df,用于根据第二个 df 中以 url 开头的第一个匹配项为第一个 df 分配类别,即输出应该是,
网址 | 类别 |
---|---|
https//abc.com | ['一个'] |
https//abcd.com | ['b'] |
https//abcd.com/aaa | ['b'] - 这个以 https//abcd.com 开头,这是第一个匹配 |
https//abc.com/abcd | ['a'] - 这个以 https//abc.com 开头,这是第一个匹配 |
目前有效的代码是这样的,
def add_category_column(df: pl.DataFrame, conditions_df) -> pl.DataFrame:
# Initialize the category column with empty lists
df = df.with_columns(pl.Series("category", [[] for _ in range(len(df))], dtype=pl.List(pl.String)))
# Apply the conditions to populate the category column
for row in conditions_df.iter_rows():
url_start, category = row
df = df.with_columns(
pl.when(
(pl.col("url").str.starts_with(url_start)) & (pl.col("category").list.len() == 0)
)
.then(pl.lit(category))
.otherwise(pl.col("category"))
.alias("category")
)
return df
但是有没有办法在不使用 for 循环的情况下实现相同的效果,我们可以在这里使用 join_where 吗,但在我的尝试中 join_where 对 starts_with 不起作用
不幸的是,目前看起来并非如此。我刚刚在 Polars 问题跟踪器上提出了这个问题并请求它。
这是@roman 答案的一个细微变化,它在连接之前准备一个行索引。
或者,如果您有更大的数据或性能问题,则通过 DuckDB 的解决方案将进行连接
starts_with
(Polars SQL 似乎还不支持它)我希望
pl.DataFrame.join_where()
能工作,但显然它还不允许pl.Expr.str.starts_with()
条件——我得到了only 1 binary comparison allowed as join condition
错误。因此你可以
pl.DataFrame.join()
改用pl.DataFrame.filter()
:您还可以将DuckDB 与 Polars 集成并使用
lateral join
:但是,您必须小心,因为在标准 SQL 规范中行集合是无序的,因此如果不在
order by
侧面添加明确的子句,我不会在生产中这样做。可以
concat
先进行水平操作(而不是初始操作)来找到匹配项join
。它需要更多的手动步骤 - 但在处理较大的输入时给了我最快的结果,所以可能会感兴趣。
解释
我们水平排列
.concat()
框架并用来.str.extract_many()
获取所有子字符串匹配的列表,然后将其explode
分成几行。然后我们应用
starts_with
过滤器和first
约束。A
.join()
用于获取对应的类别。最后
.join()
根据匹配的行将其添加回原始框架index
。