Nik*_*kSp 5 python user-defined-functions python-polars
免责声明 (1):这个问题支持这个 SO。在两位用户请求详细说明我的案例后。
免责声明 (2) - 29/11 添加:到目前为止,我已经看到了两种利用该功能的解决方案(在此 SO 中提出的以及支持性的解决方案)explode()。根据我对整个数据集(约 300 万行数据)所做的一些基准测试,RAM 确实会爆炸explode(),因此我将在数据集的样本上测试该函数,如果它有效,我将接受那些可能在较小的数据集上进行实验的人的方法解决方案表。
输入数据集(约 300 万行)来自80_000 个 IMDb 电影的 ml-latest 数据集以及 330_000 个用户的相应评分(您可以从此处ratings.csv下载 CSV 文件- 891mb)。
polars我使用like加载数据集movie_ratings = pl.read_csv(os.path.join(application_path + data_directory, "ratings.csv")),application_path并且data_directory是本地服务器上的父路径。
阅读数据集后,我的目标是生成用户在所有其他用户之间的余弦相似度。为此,首先我必须将评级表(约 300 万行)转换为每个用户 1 行的表。因此,我运行以下查询
## 1st computation bottleneck using UDF functions (2.5minutes for 250_000 rows)
users_metadata = movie_ratings.filter(
(pl.col("userId") != input_id) #input_id is a random userId. I prefer to make my tests using userId '1' so input_id=1 in this case.
).group_by("userId")\
.agg(
pl.col("movieId").unique().alias("user_movies"),
pl.col("rating").alias("user_ratings")
)\
.with_columns(
pl.col("user_movies").map_elements(
lambda row: sorted( list(set(row).intersection(set(user_rated_movies))) ), return_dtype=pl.List(pl.Int64)
).alias("common_movies")
)\
.with_columns(
pl.col("common_movies").map_elements(
lambda row: len(row), return_dtype=pl.Int64
).alias("common_movies_frequency")
)
similar_users = (
users_metadata.filter(
(pl.col("common_movies_frequency").le(len(user_rated_movies))) &
(pl.col("common_movies_frequency").gt(0)) # we don't want the users that don't have seen any movies from the ones seen/rated by the target user.
)
.sort("common_movies_frequency", descending=True)
)
## 2nd computation bottleneck using UDF functions
similar_users = (
similar_users.with_columns(
pl.struct(pl.all()).map_elements(
get_common_movie_ratings, #asked on StackOverflow
return_dtype=pl.List(pl.Float64),
strategy="threading"
).alias("common_movie_ratings")
).with_columns(
pl.struct(["common_movies"]).map_elements(
lambda row: get_target_movie_ratings(row, user_rated_movies, user_ratings),
return_dtype=pl.List(pl.Float64),
strategy="threading"
).alias("target_user_common_movie_ratings")
).with_columns(
pl.struct(["common_movie_ratings","target_user_common_movie_ratings"]).map_elements(
lambda row: compute_cosine(row),
return_dtype=pl.Float64,
strategy="threading"
).alias("similarity_score")
)
)
Run Code Online (Sandbox Code Playgroud)
上面的代码片段按 userId 对表进行分组,并计算有关它们的一些重要元数据。具体来说,
user_movies、每个用户的 user_ ratings
common_movies = 用户观看的电影与 input_id 用户(因此用户 1)观看的电影相同的交集。用户1看过的电影基本上是user_rated_movies = movie_ratings.filter(pl.col("userId") == input_id).select("movieId").to_numpy().ravel()
common_movies_Frequency = 每个用户的列长度common_movies。每个用户的长度不是固定的。
common_movie_ ratings = 我在这里询问的函数的结果
target_user_common_movie_ ratings = 与每个用户的常见电影索引相匹配的目标用户(user1)的评分。
相似度得分 = 余弦相似度得分。
表的屏幕截图(不要关注列potential recommendations)

最后,我根据用户 1 看过的users_metadata62 ( ) 部电影中 common_movies_freq 小于或等于的所有用户来过滤该表。len(user_rated_movies)总共有 250_000 个用户。
该表是我在这个问题中询问的 UDF 函数的输入数据框。使用这个数据框(~250_000 个用户),我想计算每个用户与用户 1 的余弦相似度。为此,我想比较他们的评分相似度。因此,对于每个用户通常评分的电影,计算两个评分数组之间的余弦相似度。
以下是我用来支持我的功能的三个 UDF 函数。
def get_common_movie_ratings(row) -> pl.List(pl.Float64):
common_movies = row['common_movies']
user_ratings = row['user_ratings']
ratings_for_common_movies = [user_ratings[list(row['user_movies']).index(movie)] for movie in common_movies]
return ratings_for_common_movies
def get_target_movie_ratings(row, target_user_movies:np.ndarray, target_user_ratings:np.ndarray) -> pl.List(pl.Float64):
common_movies = row['common_movies']
target_user_common_ratings = [target_user_ratings[list(target_user_movies).index(movie)] for movie in common_movies]
return target_user_common_ratings
def compute_cosine(row)->pl.Float64:
array1 = row["common_movie_ratings"]
array2 = row["target_user_common_movie_ratings"]
magnitude1 = norm(array1)
magnitude2 = norm(array2)
if magnitude1 != 0 or magnitude2 != 0: #avoid division with 0 norms/magnitudes
score: float = np.dot(array1, array2) / (norm(array1) * norm(array2))
else:
score: float = 0.0
return score
Run Code Online (Sandbox Code Playgroud)
基准测试
主要问题是如何将这 3 个 UDF 函数转换为原生的 Polars 命令。
2023-11-29 13:40:24 - INFO - 计算 254188 个用户的潜在相似用户元数据:0:02:15.586497
2023-11-29 13:40:51 - INFO - 计算 194943 个用户的相似度分数:0:00:27.472388
我们可以得出结论,代码的主要瓶颈是在创建表时user_metadata。
pl.read_csv将所有内容加载到内存中。
pl.scan_csv()相反,返回一个 LazyFrame。
pl.scan_csv("imdb.csv").sink_parquet("imdb.parquet")为了使复制结果变得更简单,我过滤了数据集pl.col("userId").is_between(1, 3)并删除了该timestamp列:
movie_ratings = pl.read_csv(\n b\'userId,movieId,rating\\n1,1,4.0\\n1,110,4.0\\n1,158,4.0\\n1,260,4.5\\n1,356,5.0\\n1,381,3.5\\n1,596,4.0\\n1,1036,5.0\\n1,1049,\'\n b\'3.0\\n1,1066,4.0\\n1,1196,3.5\\n1,1200,3.5\\n1,1210,4.5\\n1,1214,4.0\\n1,1291,5.0\\n1,1293,2.0\\n1,1376,3.0\\n1,1396,3.0\\n1,153\'\n b\'7,4.0\\n1,1909,3.0\\n1,1959,4.0\\n1,1960,4.0\\n1,2028,5.0\\n1,2085,3.5\\n1,2116,4.0\\n1,2336,3.5\\n1,2571,2.5\\n1,2671,4.0\\n1,2\'\n b\'762,5.0\\n1,2804,3.0\\n1,2908,4.0\\n1,3363,3.0\\n1,3578,5.0\\n1,4246,4.0\\n1,4306,4.0\\n1,4699,3.5\\n1,4886,5.0\\n1,4896,4.0\\n1\'\n b\',4993,4.0\\n1,4995,5.0\\n1,5952,4.5\\n1,6539,4.0\\n1,7064,3.5\\n1,7122,4.0\\n1,7139,3.0\\n1,7153,5.0\\n1,7162,4.0\\n1,7366,3.5\'\n b\'\\n1,7706,3.5\\n1,8132,5.0\\n1,8533,5.0\\n1,8644,3.5\\n1,8961,4.5\\n1,8969,4.0\\n1,8981,3.5\\n1,33166,5.0\\n1,33794,3.0\\n1,40629\'\n b\',4.5\\n1,49647,5.0\\n1,52458,5.0\\n1,53996,5.0\\n1,54259,4.0\\n2,1,5.0\\n2,2,3.0\\n2,6,4.0\\n2,10,3.0\\n2,11,3.0\\n2,17,5.0\\n2,1\'\n b\'9,3.0\\n2,21,5.0\\n2,25,3.0\\n2,31,3.0\\n2,34,5.0\\n2,36,5.0\\n2,39,3.0\\n2,47,5.0\\n2,48,2.0\\n2,50,4.0\\n2,52,3.0\\n2,58,3.0\\n2\'\n b\',95,2.0\\n2,110,5.0\\n2,111,3.0\\n2,141,5.0\\n2,150,5.0\\n2,151,5.0\\n2,153,3.0\\n2,158,3.0\\n2,160,1.0\\n2,161,3.0\\n2,165,4.0\'\n b\'\\n2,168,3.0\\n2,172,2.0\\n2,173,2.0\\n2,185,3.0\\n2,186,3.0\\n2,204,3.0\\n2,208,3.0\\n2,224,3.0\\n2,225,3.0\\n2,231,4.0\\n2,235,3\'\n b\'.0\\n2,236,2.0\\n2,252,3.0\\n2,253,2.0\\n2,256,3.0\\n2,261,4.0\\n2,265,2.0\\n2,266,4.0\\n2,282,1.0\\n2,288,1.0\\n2,292,3.0\\n2,29\'\n b\'3,3.0\\n2,296,5.0\\n2,300,4.0\\n2,315,3.0\\n2,317,3.0\\n2,318,5.0\\n2,333,3.0\\n2,337,3.0\\n2,339,5.0\\n2,344,3.0\\n2,349,4.0\\n2\'\n b\',350,3.0\\n2,356,5.0\\n2,357,5.0\\n2,364,4.0\\n2,367,4.0\\n2,377,4.0\\n2,380,4.0\\n2,420,2.0\\n2,432,3.0\\n2,434,4.0\\n2,440,3.0\'\n b\'\\n2,442,3.0\\n2,454,3.0\\n2,457,5.0\\n2,480,3.0\\n2,500,4.0\\n2,509,3.0\\n2,527,5.0\\n2,539,5.0\\n2,553,3.0\\n2,586,4.0\\n2,587,\'\n b\'4.0\\n2,588,4.0\\n2,589,4.0\\n2,590,5.0\\n2,592,3.0\\n2,593,5.0\\n2,595,4.0\\n2,597,5.0\\n2,786,4.0\\n3,296,5.0\\n3,318,5.0\\n3,8\'\n b\'58,5.0\\n3,2959,5.0\\n3,3114,5.0\\n3,3751,5.0\\n3,4886,5.0\\n3,6377,5.0\\n3,8961,5.0\\n3,60069,5.0\\n3,68954,5.0\\n3,69844,5.0\'\n b\'\\n3,74458,5.0\\n3,76093,5.0\\n3,79132,5.0\\n3,81834,5.0\\n3,88125,5.0\\n3,99114,5.0\\n3,109487,5.0\\n3,112556,5.0\\n3,115617,5.\'\n b\'0\\n3,115713,4.0\\n3,116797,5.0\\n3,119145,5.0\\n3,134853,5.0\\n3,152081,5.0\\n3,176101,5.0\\n3,177765,5.0\\n3,185029,5.0\\n3,1\'\n b\'87593,3.0\\n\'\n)\nRun Code Online (Sandbox Code Playgroud)\n我们将假设input_id == 1
收集所有所需信息的一种可能方法:
\n# Finding the intersection first seems to use ~35% less RAM\n# than the previous join / anti-join approach\nintersection = (\n movie_ratings\n .filter(\n (pl.col("userId") == 1)\n | \n ((pl.col("userId") != 1) &\n (pl.col("movieId").is_in(pl.col("movieId").filter(pl.col("userId") == 1))))\n )\n)\n\n(intersection.filter(pl.col("userId") == 1)\n .join(\n intersection.filter(pl.col("userId") != 1),\n on = "movieId"\n )\n .group_by(pl.col("userId_right").alias("other_user"))\n .agg(\n target_user = pl.first("userId"),\n common_movies = "movieId",\n common_movies_frequency = pl.count(),\n target_user_ratings = "rating",\n other_user_ratings = "rating_right",\n )\n)\nRun Code Online (Sandbox Code Playgroud)\nshape: (2, 6)\n\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\n\xe2\x94\x82 other_user \xe2\x94\x86 target_user \xe2\x94\x86 common_movies \xe2\x94\x86 common_movies_frequency \xe2\x94\x86 target_user_ratings \xe2\x94\x86 other_user_ratings \xe2\x94\x82\n\xe2\x94\x82 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x82\n\xe2\x94\x82 i64 \xe2\x94\x86 i64 \xe2\x94\x86 list[i64] \xe2\x94\x86 u32 \xe2\x94\x86 list[f64] \xe2\x94\x86 list[f64] \xe2\x94\x82\n\xe2\x95\x9e\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa1\n\xe2\x94\x82 3 \xe2\x94\x86 1 \xe2\x94\x86 [4886, 8961] \xe2\x94\x86 2 \xe2\x94\x86 [5.0, 4.5] \xe2\x94\x86 [5.0, 5.0] \xe2\x94\x82\n\xe2\x94\x82 2 \xe2\x94\x86 1 \xe2\x94\x86 [1, 110, 158, 356] \xe2\x94\x86 4 \xe2\x94\x86 [4.0, 4.0, 4.0, 5.0] \xe2\x94\x86 [5.0, 5.0, 3.0, 5.0] \xe2\x94\x82\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\nRun Code Online (Sandbox Code Playgroud)\n可能有更好的策略来并行化工作,但基线尝试可以简单地循环每个用户 ID
\nmovie_ratings = pl.scan_parquet("imdb.parquet")\n\nuser_ids = movie_ratings.select(pl.col("userId").unique()).collect().to_series()\n\nfor user_id in user_ids:\n result = (\n movie_ratings\n .filter(pl.col("userId") == user_id)\n ...\n )\n print(result.collect())\nRun Code Online (Sandbox Code Playgroud)\n我很好奇,所以决定检查duckdb进行比较。
\nimport duckdb\n\nduckdb.sql("""\nwith \n db as (from movie_ratings)\nfrom \n db target, db other\nselect \n target.userId target_user,\n other.userId other_user,\n list(other.movieId) common_movies,\n count(other.movieId) common_movies_frequency,\n list(target.rating) target_user_ratings,\n list(other.rating) other_user_ratings,\nwhere \n target_user = 1 and other_user != 1 and target.movieId = other.movieId\ngroup by \n target_user, other_user\n""").pl()\nRun Code Online (Sandbox Code Playgroud)\nshape: (2, 6)\n\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\n\xe2\x94\x82 target_user \xe2\x94\x86 other_user \xe2\x94\x86 common_movies \xe2\x94\x86 common_movies_frequency \xe2\x94\x86 target_user_ratings \xe2\x94\x86 other_user_ratings \xe2\x94\x82\n\xe2\x94\x82 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x86 --- \xe2\x94\x82\n\xe2\x94\x82 i64 \xe2\x94\x86 i64 \xe2\x94\x86 list[i64] \xe2\x94\x86 i64 \xe2\x94\x86 list[f64] \xe2\x94\x86 list[f64] \xe2\x94\x82\n\xe2\x95\x9e\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xaa\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa1\n\xe2\x94\x82 1 \xe2\x94\x86 3 \xe2\x94\x86 [4886, 8961] \xe2\x94\x86 2 \xe2\x94\x86 [5.0, 4.5] \xe2\x94\x86 [5.0, 5.0] \xe2\x94\x82\n\xe2\x94\x82 1 \xe2\x94\x86 2 \xe2\x94\x86 [1, 110, 356, 158] \xe2\x94\x86 4 \xe2\x94\x86 [4.0, 4.0, 5.0, 4.0] \xe2\x94\x86 [5.0, 5.0, 5.0, 3.0] \xe2\x94\x82\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\nRun Code Online (Sandbox Code Playgroud)\n针对完整数据集运行这两个示例(运行时间基本相同)我得到:
\nimport rich.filesize\n\nprint("duckdb:", rich.filesize.decimal(223232000))\nprint("polars:", rich.filesize.decimal(1772072960))\nRun Code Online (Sandbox Code Playgroud)\nduckdb: 223.2 MB\npolars: 1.8 GB\nRun Code Online (Sandbox Code Playgroud)\n所以看来Polars方面还有潜在的改进空间。
\n