将spark DataFrame列转换为python列表

时间:2016-07-27 10:36:18

标签: python apache-spark pyspark spark-dataframe

我处理的数据框有两列,mvv和count。

+---+-----+
|mvv|count|
+---+-----+
| 1 |  5  |
| 2 |  9  |
| 3 |  3  |
| 4 |  1  |

我想获得两个包含mvv值和计数值的列表。像

这样的东西
mvv = [1,2,3,4]
count = [5,9,3,1]

所以,我尝试了以下代码:第一行应该返回一个python列表行。我想看到第一个值:

mvv_list = mvv_count_df.select('mvv').collect()
firstvalue = mvv_list[0].getInt(0)

但是我收到第二行的错误消息:

  

AttributeError:getInt

10 个答案:

答案 0 :(得分:82)

看,为什么你这样做不起作用。首先,您试图从Row类型获取整数,您的收集的输出是这样的:

>>> mvv_list = mvv_count_df.select('mvv').collect()
>>> mvv_list[0]
Out: Row(mvv=1)

如果你采取这样的事情:

>>> firstvalue = mvv_list[0].mvv
Out: 1

您将获得mvv值。如果你想要数组的所有信息,可以采用以下方法:

>>> mvv_array = [int(row.mvv) for row in mvv_list.collect()]
>>> mvv_array
Out: [1,2,3,4]

但如果你对另一列尝试相同的话,你会得到:

>>> mvv_count = [int(row.count) for row in mvv_list.collect()]
Out: TypeError: int() argument must be a string or a number, not 'builtin_function_or_method'

这是因为count是内置方法。该列与count的名称相同。执行此操作的变通方法是将count的列名更改为_count

>>> mvv_list = mvv_list.selectExpr("mvv as mvv", "count as _count")
>>> mvv_count = [int(row._count) for row in mvv_list.collect()]

但是不需要这种解决方法,因为您可以使用字典语法访问该列:

>>> mvv_array = [int(row['mvv']) for row in mvv_list.collect()]
>>> mvv_count = [int(row['count']) for row in mvv_list.collect()]

它最终会奏效!

答案 1 :(得分:60)

在一个班轮后面给出你想要的清单。

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()

答案 2 :(得分:13)

这将为您提供列表中的所有元素。

mvv_list = list(
    mvv_count_df.select('mvv').toPandas()['mvv']
)

答案 3 :(得分:11)

以下代码可以帮助您

mvv_count_df.select('mvv').rdd.map(lambda row : row[0]).collect()

答案 4 :(得分:5)

我进行了基准分析,list(mvv_count_df.select('mvv').toPandas()['mvv'])是最快的方法。我很惊讶。

我使用5节点i3.xlarge群集(每个节点具有30.5 GB的RAM和4个内核)和Spark 2.4.5对10万/亿行数据集运行了不同的方法。数据均匀地分布在20个快速压缩的Parquet文件中,并且只有一列。

以下是基准测试结果(运行时间以秒为单位):

+-------------------------------------------------------------+---------+-------------+
|                          Code                               | 100,000 | 100,000,000 |
+-------------------------------------------------------------+---------+-------------+
| df.select("col_name").rdd.flatMap(lambda x: x).collect()    |     0.4 | 55.3        |
| list(df.select('col_name').toPandas()['col_name'])          |     0.4 | 17.5        |
| df.select('col_name').rdd.map(lambda row : row[0]).collect()|     0.9 | 69          |
| [row[0] for row in df.select('col_name').collect()]         |     1.0 | OOM         |
| [r[0] for r in mid_df.select('col_name').toLocalIterator()] |     1.2 | *           |
+-------------------------------------------------------------+---------+-------------+

* cancelled after 800 seconds

在驱动程序节点上收集数据时要遵循的黄金规则:

  • 尝试使用其他方法解决问题。将数据收集到驱动程序节点是昂贵的,没有利用Spark集群的功能,因此应尽可能避免。
  • 收集尽可能少的行。在收集数据之前,对列进行聚合,重复数据删除,过滤和修剪。尽可能少地将数据发送到驱动程序节点。

toPandas was significantly improved in Spark 2.3。如果您使用的Spark版本早于2.3,则可能不是最佳方法。

有关更多详细信息/基准测试结果,请参见here

答案 5 :(得分:3)

如果您收到以下错误:

  

AttributeError:' list'对象没有属性'收集'

此代码将解决您的问题:

mvv_list = mvv_count_df.select('mvv').collect()

mvv_array = [int(i.mvv) for i in mvv_list]

答案 6 :(得分:2)

可能的解决方案是使用collect_list()中的pyspark.sql.functions函数。这会将所有列值聚合到一个pyspark数组中,该数组在收集时会转换为python列表:

mvv_list   = df.select(collect_list("mvv")).collect()[0][0]
count_list = df.select(collect_list("count")).collect()[0][0] 

答案 7 :(得分:1)

根据我的数据,我得到了这些基准:

>>> data.select(col).rdd.flatMap(lambda x: x).collect()

0.52秒

>>> [row[col] for row in data.collect()]

0.271秒

>>> list(data.select(col).toPandas()[col])

0.427秒

结果相同

答案 8 :(得分:1)

让我们创建有问题的数据框

df_test = spark.createDataFrame(
    [
        (1, 5),
        (2, 9),
        (3, 3),
        (4, 1),
    ],
    ['mvv', 'count']
)
df_test.show()

哪个给

+---+-----+
|mvv|count|
+---+-----+
|  1|    5|
|  2|    9|
|  3|    3|
|  4|    1|
+---+-----+

然后应用rdd.flatMap(f).collect()获取列表

test_list = df_test.select("mvv").rdd.flatMap(list).collect()
print(type(test_list))
print(test_list)

给出

<type 'list'>
[1, 2, 3, 4]

答案 9 :(得分:0)

尽管有很多答案,但当您需要将列表与 whenisin 命令结合使用时,其中一些将不起作用。生成扁平值列表的最简单而有效的方法是使用列表理解和 [0] 来避免行名称:

flatten_list_from_spark_df=[i[0] for i in df.select("your column").collect()]

另一种方法是使用panda数据框,然后使用list函数,但是没有这个方便有效。a