PyArrow中列表类型数据的频率统计与聚合

DDD
发布: 2025-09-04 17:43:01
原创
311人浏览过

PyArrow中列表类型数据的频率统计与聚合

本文探讨了在PyArrow中对列表(List)类型数据进行分组聚合时遇到的挑战,特别是group_by操作对列表键类型的限制。针对这一问题,教程提供了一种有效的解决方案:通过将列表中的每个元素转换为独立的标量列,从而实现对列表内容的精确分组和频率统计,并详细介绍了实现步骤、关键函数及注意事项。

PyArrow中列表类型分组聚合的挑战

在使用pyarrow处理数据时,我们经常需要根据某些列的值对数据进行分组,并计算每组的聚合统计量。然而,当分组键(grouping key)中包含列表(list)类型的数据时,pyarrow的group_by方法会抛出arrownotimplementederror,提示“keys of type list”不被支持。这意味着我们无法直接使用列表类型的列作为分组依据来统计其频率。

考虑以下原始数据结构,其中ListData列是整数列表:

import pyarrow as pa
import pyarrow.compute as pc

test_table_orig = pa.table([
    pa.array(["a", "a", "a", "a", "a", "b", "b", "b", "b", "b", "c", "c", "c", "c", "c", "d", "d", "d", "d", "e", "e", "e", "e", "e", "f", "f", "f", "f", "f", "f"]),
    pa.array([[1,1,1,1], [2,0,1,2], [3,2,1,0], [4,3,2,1], [4,3,2,1], [1,2,3,4], [1,2,3,4], [1,2,3,4], [1,2,3,4], [1,2,3,4], [5,4,3,2], [5,4,3,2], [5,4,3,2], [5,4,3,2], [4,3,2,1], [6,5,4,3], [6,5,4,3], [8,7,6,5], [9,8,7,6], [7,6,5,4], [7,6,5,4], [7,6,5,4], [7,6,5,4], [10,11,12,13], [11,12,13,14], [12,13,14,15], [33,44,55,66], [22,33,44,55], [55,66,77,88], [22,33,44,55]])
], names=["ID", "ListData"])

# 尝试直接分组聚合会导致错误
try:
    test_table_orig.group_by(['ID','ListData']).aggregate([('ListData','count')]).to_pandas()
except pa.lib.ArrowNotImplementedError as e:
    print(f"Error: {e}")
# Error: Keys of type list<item: int64>
登录后复制

上述代码清晰地展示了PyArrow对列表类型作为分组键的限制。

字符串转换的权宜之计及其局限性

一种常见的变通方法是将列表数据转换为字符串表示。由于字符串是可哈希的标量类型,PyArrow可以成功地对其进行分组聚合。

test_table_string = pa.table([
    pa.array(["a", "a", "a", "a", "a", "b", "b", "b", "b", "b", "c", "c", "c", "c", "c", "d", "d", "d", "d", "e", "e", "e", "e", "e", "f", "f", "f", "f", "f", "f"]),
    pa.array(["[1,1,1,1]", "[2,0,1,2]", "[3,2,1,0]", "[4,3,2,1]", "[4,3,2,1]", "[1,2,3,4]", "[1,2,3,4]", "[1,2,3,4]", "[1,2,3,4]", "[1,2,3,4]", "[5,4,3,2]", "[5,4,3,2]", "[5,4,3,2]", "[5,4,3,2]", "[4,3,2,1]", "[6,5,4,3]", "[6,5,4,3]", "[8,7,6,5]", "[9,8,7,6]", "[7,6,5,4]", "[7,6,5,4]", "[7,6,5,4]", "[7,6,5,4]", "[10,11,12,13]", "[11,12,13,14]", "[12,13,14,15]", "[33,44,55,66]", "[22,33,44,55]", "[55,66,77,88]", "[22,33,44,55]"])
], names=["ID", "ListData"])

result_string_groupby = test_table_string.group_by(['ID','ListData']).aggregate([('ListData','count')]).to_pandas()
print("字符串转换后的分组结果:")
print(result_string_groupby)
登录后复制

字符串转换后的分组结果:

   ID       ListData  ListData_count
0   a      [1,1,1,1]               1
1   a      [2,0,1,2]               1
2   a      [3,2,1,0]               1
3   a      [4,3,2,1]               2
4   b      [1,2,3,4]               5
5   c      [5,4,3,2]               4
6   c      [4,3,2,1]               1
7   d      [6,5,4,3]               2
8   d      [8,7,6,5]               1
9   d      [9,8,7,6]               1
10  e      [7,6,5,4]               4
11  e  [10,11,12,13]               1
12  f  [11,12,13,14]               1
13  f  [12,13,14,15]               1
14  f  [33,44,55,66]               1
15  f  [22,33,44,55]               2
16  f  [55,66,77,88]               1
登录后复制

这种方法在功能上能够实现目标,但对于包含大量元素或每个元素本身就很长的列表,将其转换为字符串会带来显著的性能和内存开销。例如,一个包含120个数字,每个数字12字符的列表,转换为字符串后可能长达2.4KB。在大规模数据集上,这种转换可能导致内存溢出或处理速度急剧下降。

核心解决方案:将列表元素转换为独立列

为了高效地处理列表类型数据的分组聚合,尤其是在列表长度固定的情况下,我们可以采用“扁平化”策略:将列表中的每个元素提取出来,作为新的独立标量列。这样,group_by操作就可以在这些标量列上执行,而不会遇到类型限制。

步骤一:创建新的标量列

使用pyarrow.compute.list_element函数可以按索引提取列表中的单个元素。对于固定长度的列表,我们可以为每个索引创建一个新的列。

# 假设列表长度为4,创建4个新列
columns = {f'c{i}': pc.list_element(test_table_orig['ListData'], i) for i in range(4)}
登录后复制

这里,f'c{i}'会生成c0, c1, c2, c3等列名,每个列名对应ListData中特定索引的元素。

步骤二:构建新的扁平化表

将原始的ID列与新创建的标量列组合起来,形成一个新的PyArrow表。

序列猴子开放平台
序列猴子开放平台

具有长序列、多模态、单模型、大数据等特点的超大规模语言模型

序列猴子开放平台 0
查看详情 序列猴子开放平台
pivot_table = pa.table({'ID': test_table_orig['ID']} | columns)
print("\n扁平化后的表格结构:")
print(pivot_table.schema)
print("\n扁平化后的表格数据(前5行):")
print(pivot_table.slice(0, 5).to_pandas())
登录后复制

扁平化后的表格结构:

ID: string
c0: int64
c1: int64
c2: int64
c3: int64
登录后复制

扁平化后的表格数据(前5行):

  ID  c0  c1  c2  c3
0  a   1   1   1   1
1  a   2   0   1   2
2  a   3   2   1   0
3  a   4   3   2   1
4  a   4   3   2   1
登录后复制

步骤三:执行分组聚合

现在,pivot_table中的所有列都是标量类型。我们可以使用group_by方法对所有这些列进行分组,并计算每组的行数,从而得到原始列表的频率。

# 对所有列进行分组,并计算每组的行数
counts = pivot_table.group_by(pivot_table.column_names).aggregate([([],'count_all')])
result_list_groupby = counts.to_pandas()
print("\n扁平化后分组聚合结果:")
print(result_list_groupby)
登录后复制

扁平化后分组聚合结果:

   ID  c0  c1  c2  c3  count_all
0   a   1   1   1   1          1
1   a   2   0   1   2          1
2   a   3   2   1   0          1
3   a   4   3   2   1          2
4   b   1   2   3   4          5
5   c   5   4   3   2          4
6   c   4   3   2   1          1
7   d   6   5   4   3          2
8   d   8   7   6   5          1
9   d   9   8   7   6          1
10  e   7   6   5   4          4
11  e  10  11  12  13          1
12  f  11  12  13  14          1
13  f  12  13  14  15          1
14  f  33  44  55  66          1
15  f  22  33  44  55          2
16  f  55  66  77  88          1
登录后复制

这个结果与字符串转换方法得到的结果在逻辑上是等价的,但避免了不必要的字符串转换开销。

关键函数解析:pyarrow.compute.list_element

pyarrow.compute.list_element(array, index)是一个非常实用的函数,用于从列表类型数组(或列)中提取指定索引位置的元素。

  • array: 待操作的列表类型PyArrow数组或表列。
  • index: 要提取的元素的索引。可以是单个整数,也可以是一个整数数组,表示对每个列表元素应用不同的索引。在我们的例子中,我们使用固定的整数索引来提取每个位置的元素。

通过循环使用此函数,我们可以将一个列表列“解构”成多个标量列。

注意事项与最佳实践

  1. 列表长度的固定性:此方法最适用于列表长度固定的场景。如果列表长度可变,pc.list_element在访问超出范围的索引时会返回null。对于长度不定的列表,可能需要先确定最大长度,或者考虑其他更复杂的处理方式(如先将列表展平为多行,再进行聚合)。
  2. 性能考量
    • 优点:避免了昂贵的字符串转换和解析,直接操作数值类型,通常效率更高。
    • 缺点:如果列表非常长,将其展开为大量列可能会导致表变得非常宽,这可能会影响某些操作的性能和内存使用。在极端情况下,过宽的表可能不如其他方法高效。
  3. 内存使用:将列表元素转换为独立列会增加表的列数,但每列存储的是标量数据,通常比存储长字符串更节省内存。然而,如果原始列表的元素数量非常大(例如,几百个元素),则创建相同数量的新列可能会显著增加内存占用
  4. 结果解读:聚合后的结果中,原始的列表数据被拆分成了c0, c1, c2, c3等列。如果需要将这些列重新组合成列表形式,可以使用pyarrow.compute.make_list等函数。

总结

在PyArrow中统计列表类型数据的频率,直接使用group_by是不支持的。通过将列表中的每个固定位置的元素提取为独立的标量列,然后对这些新生成的标量列进行分组聚合,可以有效地解决这一问题。这种方法避免了字符串转换带来的性能和内存开销,为处理大规模列表数据提供了一种高效且专业的解决方案。在应用此方法时,需要注意列表长度的固定性,并根据实际数据规模权衡性能与内存。

以上就是PyArrow中列表类型数据的频率统计与聚合的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号