为了账号安全,请及时绑定邮箱和手机立即绑定

通过解决Advent of Code编程谜题来学习Snowpark(第5部分)

使用数据框解决 2024 年 Advent of Code 第一天的谜题的解决方法

照片由 Ray Hennessy 拍摄于 Unsplash

这是系列文章的下一章节,我们将通过解决Advent of Code的编程挑战来学习Snowpark。如果你错过了前几部分,可以在这里查看第一部分第二部分第三部分第四部分

最后,Advent of Code 2024 的第一道题正式上线!

和往常一样,第一天 的题目很简单,所以本课的重点是如何在 Snowpark 中实现这个解决方案,而是更多地关注实现过程。

小提示: 实现软件解决方案通常不止一种方法。我选择了这种解决方案,并解释了原因,但这肯定不是唯一的选择。你可以自由尝试和探索,试试看练习Snowpark!

导入示例数据集

我们先来看看示例数据(顺便也看看你的输入数据,看看它是否和示例数据类似,因为你后面需要用到它)。

以下是数字对序列:
3   4  
4   3  
2   5  
1   3  
3   9  
3   3
这些数字可能代表坐标或分数等,具体含义请参照上下文。```

这看起来是一个结构良好的数据,包含两列,每列之间有三个空格的间隔,这意味着我们可以将这些数据视为两列名为LOC1和LOC2的数据,并将三个空格作为分隔符。我们可以在“读取文件内容到数据框”部分(系列的第四部分:https://medium.com/@majaf/learn-snowpark-by-solving-the-advent-of-code-programming-puzzles-part-4-df32a5e850dd)中提到的方法将数据导入数据框中。

从复制示例数据并将复制的数据保存到名为 _AOC01_example.txt_ 的文件开始,然后将文件上传到 `aoc_files_stage`。

session.file.put("AOC01_example.txt", "@aoc_files_stage", auto_compress=False)
// 将 "AOC01_example.txt" 文件存入 "@aoc_files_stage" 位置,不进行自动压缩


接下来定义文件的结构如下:

csv模式 = 结构类型([
结构字段定义("LOC1", 整数类型()),
结构字段定义("LOC2", 整数类型())
])


将由 `file_name` 变量引用的文件按阶段读入,并命名为 _df_ 的 DataFrame 中:

df = session.read.option("field_delimiter", " ") \
.schema(schema_for_csv).csv(f"@aoc_files_stage/{file_name}").collect()

/* 读取文件并指定列分隔符为三个空格,使用给定的schema_for_csv模式读取csv文件,文件路径为"@aoc_files_stage/{file_name}",然后收集结果到DataFrame中。 */

## 实施方案

从谜题描述中,我们得知需要将数据的每一列分别存储在单独的变量中,因此我们创建了两个列表,分别命名为 `list1` 和 `list2` 用于存储这些值(参见 [Part 4](https://medium.com/@majaf/learn-snowpark-by-solving-the-advent-of-code-programming-puzzles-part-4-df32a5e850dd) 以了解我们如何从数据帧中提取这些列表)。
# list1 存储 df 中每个元素的 "LOC1" 字段值
list1 = [l.asDict()["LOC1"] for l in df]  
# list2 存储 df 中每个元素的 "LOC2" 字段值
list2 = [l.asDict()["LOC2"] for l in df]

有了这两份列表,我们简单地用 Python 编写解决谜题的代码,而不需要额外的 Snowpark 相关知识。就像在该系列中第 3 部分一样,我们将通过创建一个名为 _AOC01_part1_part2.py_ 的单独 Python 文件来进行代码的模块化,在该文件中我们将定义一个名为 `Part1Part2()` 的类,其中包含两个方法,分别名为 `part1()` 和 `part2()`。每个方法都将包含谜题的答案。

你可以自己动手解决问题,但如果卡住了,你可以在我 GitHub 仓库里找到这个谜题的答案。[here](https://github.com/mferle/AOC2024/blob/main/day01/AOC01.py)

别忘了把你输入的数据保存到一个叫_AOC01_input.txt_的文件里,把文件上传到内部阶段,在用示例数据测试完之后,运行你的代码处理输入数据。

## 我们能用其他方式解开这个谜题吗?

当然我们能。虽然我们将数据文件读入了一个Snowpark数据框,但我们并没有用数据框来做进一步的处理。我们只是从数据框中取出了数据,将其转换成两个Python列表,在Python里处理了方案。这听起来像是错失了一个机会,所以让我们想想如何用数据框操作来解决这个谜题。

对于谜题的第一部分,使用Snowpark数据帧来解决会更复杂。原因是Snowpark数据帧的行为类似于关系数据集(你可以将其想象成一个表格),无法单独处理每一列。而谜题要求我们独立地对每一列进行排序,但是数据帧上没有提供这样的操作方法。

一个选择是使用Pandas DataFrame,因为支持单独操作列并结合结果。让我们用Pandas DataFrame来解决第一部分。

## 用Pandas数据框搞定第一部分的问题

第一步是将存储在 _df_ 变量中的 Snowpark 数据框转换成名为 _dfp_ 的 Pandas 数据框。
# 转换为Pandas数据帧  
dfp = df.to_pandas()

我们可以使用 `sort_values()` 方法对数据框中的每一列进行排序,并设置 `ignore_index=True` 参数,因为我们不想重新标记轴,每列是独立排序的。
对每一列进行排序操作

loc1sorted = dfp["LOC1"].sort_values(ignore_index=True)
loc2sorted = dfp["LOC2"].sort_values(ignore_index=True)

或简化为:
对“LOC1”列进行排序后,对“LOC2”列做同样的操作

loc1sorted = dfp["LOC1"].sort_values(ignore_index=True)
loc2sorted = dfp["LOC2"].sort_values(ignore_index=True)


将两个已经排序的列合并成一个Pandas DataFrame:
从排序后的列创建一个新的Pandas数据帧,如下

dfs = pd.DataFrame()
dfs["LOC1"] = loc1sorted
dfs["LOC2"] = loc2sorted


添加一个用来计算差值的列:
# 计算差
dfs['DIFF'] = abs(dfs["LOC1"] - dfs["LOC2"])

把差异加起来存到 `part1_answer` 里,然后。
# 求和
part1_answer = dfs["差"].sum()

这用Pandas中的数据框解决了谜题的第一部分。

那谜题的第二部分呢?我们可以用Snowpark数据框来做这部分。

## 用 Snowpark 数据框搞定第二部分

从读取 staged 文件数据的 _df_ 数据框出发,创建另一个数据框,命名为 _dfg_,统计每个地点出现的次数:
# 按 LOC2 分组并计数
dfg = df.groupby("LOC2").count()

然后将 _df_ 数据框与 _dfg_ 数据框内连接起来,这样得到一个新数据框,命名为 _dfj_ :
# 将原始 df 数据框与分组和汇总后的 dfg 数据框使用内连接合并,并选择 'LOC1' 列和 'COUNT' 列
dfj = df.join(dfg, df.col('LOC1') == dfg.col('LOC2')).select(df.col('LOC1'), dfg.col('COUNT'))

使用 `select_expr()` 方法将 _dfj_ 数据框中的各列数值相乘,然后使用 `agg()` 方法求和。
# 计算LOC1和COUNT列相乘的结果并求和
df_sum = dfj.select_expr("LOC1 * COUNT").agg("$1", "sum").collect()

这已经给出了结果,但是它是数据框的形式,让我们把它提取出来。
# 提取第一列的第一个值作为答案  
part2_answer = df_sum[0].asDict()["SUM($1)"]


好了,完成了!您可以在我的GitHub仓库中找到这段使用Snowpark数据帧方法的第二个解决方案的完整代码。[这里](https://github.com/mferle/AOC2024/blob/main/day01/AOC01_data_frame.py)。

## Snowflake 的文档,涵盖本课内容的主题

* 在 Snowpark Python 中处理 DataFrame — [指定如何转换数据集](https://docs.snowflake.com/en/developer-guide/snowpark/python/working-with-dataframes#specifying-how-the-dataset-should-be-transformed)
* 在 Snowpark Python 中处理 DataFrame — [将 DataFrame 的内容转换为 Pandas DataFrame](https://docs.snowflake.com/en/developer-guide/snowpark/python/working-with-dataframes#return-the-contents-of-a-dataframe-as-a-pandas-dataframe)

我是 Maja Ferle,[Snowflake 数据超级英雄](https://www.snowflake.com/en/data-superheroes/),也是一位 [In516ht](https://www.in516ht.com/) 的资深顾问。你可以在 LinkedIn 上联系我,或者阅读我的新书《Snowflake 数据工程》([Snowflake Data Engineering](https://www.manning.com/books/snowflake-data-engineering))。
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消