将spark DataFrame列转换为python列表

将spark DataFrame列转换为python列表,python,apache-spark,pyspark,spark-dataframe,Python,Apache Spark,Pyspark,Spark Dataframe,我在一个有两列的数据帧上工作,mvv和count +---+-----+ |mvv|count| +---+-----+ | 1 | 5 | | 2 | 9 | | 3 | 3 | | 4 | 1 | mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect() 我想获得两个包含mvv值和计数值的列表。差不多 mvv = [1,2,3,4] count = [5,9,3,1] mvv = mvv_co

我在一个有两列的数据帧上工作,mvv和count

+---+-----+
|mvv|count|
+---+-----+
| 1 |  5  |
| 2 |  9  |
| 3 |  3  |
| 4 |  1  |
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
我想获得两个包含mvv值和计数值的列表。差不多

mvv = [1,2,3,4]
count = [5,9,3,1]
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
因此,我尝试了以下代码:第一行应该返回一个python行列表。我想看到第一个值:

mvv_list = mvv_count_df.select('mvv').collect()
firstvalue = mvv_list[0].getInt(0)
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
但我在第二行收到一条错误消息:

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

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

你看,为什么你这样做是行不通的。首先,您试图从类型中获取整数,collect的输出如下所示:

>>> mvv_list = mvv_count_df.select('mvv').collect()
>>> mvv_list[0]
Out: Row(mvv=1)
>>> firstvalue = mvv_list[0].mvv
Out: 1
>>> mvv_array = [int(row.mvv) for row in mvv_list.collect()]
>>> mvv_array
Out: [1,2,3,4]
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
如果你采取这样的措施:

>>> mvv_list = mvv_count_df.select('mvv').collect()
>>> mvv_list[0]
Out: Row(mvv=1)
>>> firstvalue = mvv_list[0].mvv
Out: 1
>>> mvv_array = [int(row.mvv) for row in mvv_list.collect()]
>>> mvv_array
Out: [1,2,3,4]
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
您将获得
mvv
值。如果需要阵列的所有信息,可以采用以下方式:

>>> mvv_list = mvv_count_df.select('mvv').collect()
>>> mvv_list[0]
Out: Row(mvv=1)
>>> firstvalue = mvv_list[0].mvv
Out: 1
>>> mvv_array = [int(row.mvv) for row in mvv_list.collect()]
>>> mvv_array
Out: [1,2,3,4]
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
但如果对另一列尝试相同的方法,则会得到:

>>> 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'
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
这是因为
count
是一个内置方法。该列的名称与
count
相同。解决方法是将
count
的列名更改为
\u count

>>> mvv_list = mvv_list.selectExpr("mvv as mvv", "count as _count")
>>> mvv_count = [int(row._count) for row in mvv_list.collect()]
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
但不需要此解决方法,因为您可以使用字典语法访问该列:

>>> mvv_array = [int(row['mvv']) for row in mvv_list.collect()]
>>> mvv_count = [int(row['count']) for row in mvv_list.collect()]
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()

它最终会起作用的

下面的一行给出了您想要的列表

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

如果出现以下错误:

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
AttributeError:“list”对象没有属性“collect”

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
此代码将解决您的问题:

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

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

下面的代码将帮助您

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

这将以列表的形式提供所有元素

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
mvv_list = list(
    mvv_count_df.select('mvv').toPandas()['mvv']
)

根据我的数据,我得到了以下基准:

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
>>> data.select(col).rdd.flatMap(lambda x: x).collect()
0.52秒

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
>>> [row[col] for row in data.collect()]
0.271秒

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
>>> list(data.select(col).toPandas()[col])
0.427秒

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

结果是一样的

我进行了一次基准测试分析,
列表(mvv\u count\u df.select('mvv')。toPandas()['mvv'])
是最快的方法。我很惊讶

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
我使用Spark 2.4.5的5节点i3.xlarge集群(每个节点有30.5 Gb的RAM和4个内核)在10万/1亿行数据集上运行了不同的方法。数据平均分布在20个snappy压缩拼花地板文件中,只有一列

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
以下是基准测试结果(以秒为单位的运行时):

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
在驱动程序节点上收集数据时要遵循的黄金规则:

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
  • 试着用其他方法解决这个问题。向驱动程序节点收集数据成本高昂,无法利用Spark cluster的电源,应尽可能避免
  • 收集尽可能少的行。在收集数据之前,对列进行聚合、重复数据消除、筛选和修剪。尽可能少地向驱动程序节点发送数据
toPandas
。如果您使用的是早于2.3的Spark版本,那么这可能不是最好的方法

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

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

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

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
mvv_list=df.select(collect_list(“mvv”)).collect()[0][0]
count_list=df.select(collect_list(“count”)).collect()[0][0]

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

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
df_test = spark.createDataFrame(
    [
        (1, 5),
        (2, 9),
        (3, 3),
        (4, 1),
    ],
    ['mvv', 'count']
)
df_test.show()

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
+---+-----+
|mvv|count|
+---+-----+
|  1|    5|
|  2|    9|
|  3|    3|
|  4|    1|
+---+-----+
<type 'list'>
[1, 2, 3, 4]
然后应用rdd.flatMap(f).collect()获取列表

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
test_list = df_test.select("mvv").rdd.flatMap(list).collect()
print(type(test_list))
print(test_list)

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
+---+-----+
|mvv|count|
+---+-----+
|  1|    5|
|  2|    9|
|  3|    3|
|  4|    1|
+---+-----+
<type 'list'>
[1, 2, 3, 4]

[1, 2, 3, 4]

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

mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()
flatten_list_from_spark_df=[i[0] for i in df.select("your column").collect()]

另一种方法是使用panda data frame,然后使用
list
函数,但它并不方便,也不如此有效。a

它对第一列非常有效,但对列计数不起作用,我认为这是因为(spark的函数计数)您可以添加您对计数做了什么?在评论中添加这里。感谢您的回复,所以这行工作mvv_list=[int(i.mvv)for i in mvv_count.选择('mvv').collect()]但不是这一个count_list=[int(i.count)for i in mvv_count.选择('count')。collect()]返回无效的语法不需要添加这个
select('count')
这样使用:
count_list=[int(i.count)对于mvv_列表中的i.collect()]
我将在响应中添加示例。@a.moussa
[i.['count']对于mvv_列表中的i.collect()]
可以明确使用名为“count”的列,而不是
count
函数性能方面,此解决方案比您的解决方案mvv_列表=[int(i.mvv])快得多对于mvv_count.select('mvv').collect()]中的i,这难道不适用于OP的问题吗?:mvv=mvv_count_df.select(“mvv”).rdd.flatMap(list).collect()这应该是可以接受的答案。原因是,在整个过程中,您始终处于spark上下文中,然后在最后收集,而不是提前退出spark上下文,这可能会导致更大的收集,具体取决于您所做的。我也遇到了这个错误,此解决方案解决了问题。但是为什么我会出错呢?(许多其他人似乎不明白!)如果您使用
tolocaterator
而不是
collect
,它的内存效率应该更高
[row[col]用于数据中的row.tolocaterator()]
自Spark 2.3以来,此代码是最快、最不可能导致OutOfMemory异常的代码:
列表(df.select('mvv').toPandas()['mvv']))
。这大大加快了toPandas的速度。如果您使用的是Spark 2.3+,请不要使用其他方法。有关更多基准测试的详细信息,请参阅我的答案。这是Spark 2.3+最快、最有效的解决方案。请参见我的答案中的基准测试结果。
mvv = mvv_count_df.select("mvv").rdd.flatMap(lambda x: x).collect()