Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 通过基于另一个变量保留顺序来收集_列表_Python_Apache Spark_Pyspark - Fatal编程技术网

Python 通过基于另一个变量保留顺序来收集_列表

Python 通过基于另一个变量保留顺序来收集_列表,python,apache-spark,pyspark,Python,Apache Spark,Pyspark,我试图在Pyspark中使用现有列集合上的groupby聚合创建一个新的列表列。下面提供了一个示例输入数据帧: ------------------------ id | date | value ------------------------ 1 |2014-01-03 | 10 1 |2014-01-04 | 5 1 |2014-01-05 | 15 1 |2014-01-06 | 20 2 |2014-02-10 | 100 2 |2

我试图在Pyspark中使用现有列集合上的groupby聚合创建一个新的列表列。下面提供了一个示例输入数据帧:

------------------------
id | date        | value
------------------------
1  |2014-01-03   | 10 
1  |2014-01-04   | 5
1  |2014-01-05   | 15
1  |2014-01-06   | 20
2  |2014-02-10   | 100   
2  |2014-03-11   | 500
2  |2014-04-15   | 1500
预期产出为:

id | value_list
------------------------
1  | [10, 5, 15, 20]
2  | [100, 500, 1500]
列表中的值按日期排序

我尝试使用collect_列表,如下所示:

from pyspark.sql import functions as F
ordered_df = input_df.orderBy(['id','date'],ascending = True)
grouped_df = ordered_df.groupby("id").agg(F.collect_list("value"))
但是,即使我在聚合之前按日期对输入数据帧进行排序,collect_list也不能保证顺序


是否有人可以帮助您保存基于第二个(日期)变量的顺序来进行聚合?

如果您将日期和值收集为一个列表,您可以使用和
udf
根据日期对结果列进行排序,然后只保留结果中的值

import operator
import pyspark.sql.functions as F

# create list column
grouped_df = input_df.groupby("id") \
               .agg(F.collect_list(F.struct("date", "value")) \
               .alias("list_col"))

# define udf
def sorter(l):
  res = sorted(l, key=operator.itemgetter(0))
  return [item[1] for item in res]

sort_udf = F.udf(sorter)

# test
grouped_df.select("id", sort_udf("list_col") \
  .alias("sorted_list")) \
  .show(truncate = False)
+---+----------------+
|id |sorted_list     |
+---+----------------+
|1  |[10, 5, 15, 20] |
|2  |[100, 500, 1500]|
+---+----------------+

为了确保对每个id进行排序,我们可以使用sortWithinPartitions:

from pyspark.sql import functions as F
ordered_df = (
    input_df
        .repartition(input_df.id)
        .sortWithinPartitions(['date'])


)
grouped_df = ordered_df.groupby("id").agg(F.collect_list("value"))

这个问题是针对PySpark提出的,但如果将它也用于Scala Spark,可能会有所帮助

让我们准备测试数据帧: 使用自定义项 使用窗口
Window
用户提供的示例通常不能真正解释发生了什么,所以让我为您分析一下

如您所知,将
collect_list
groupBy
一起使用将导致无序的值列表。这是因为根据数据的分区方式,Spark在组中找到一行后会立即将值附加到列表中。然后,顺序取决于Spark如何计划对执行者的聚合

窗口
功能允许您控制这种情况,将行按特定值分组,以便您可以对每个结果组执行操作:

w = Window.partitionBy('id').orderBy('date')
  • partitionBy
    -您希望使用相同的
    id
  • orderBy
    -您希望组中的每一行按
    date
一旦定义了窗口的范围—“具有相同
id
,按
date
排序的行”-,您就可以使用它对其执行操作,在本例中是一个
收集列表

F.collect_list('value').over(w)
此时,您创建了一个新列
sorted_list
,其中包含按日期排序的有序值列表,但每个
id
仍有重复的行。要删除要按
groupBy
id
删除的重复行,并为每个组保留
max
值,请执行以下操作:

.groupBy('id')\
.agg(F.max('sorted_list').alias('sorted_list'))
作为补充,我在Spark上测试了sortWithinPartitions和GroupBy的使用情况,发现它的性能比窗口函数或UDF要好得多。尽管如此,在使用这种方法时,每个分区仍然存在一个错误排序的问题,但它可以很容易地解决。我在这里展示


此方法在大型数据帧上特别有用,但如果驱动程序内存不足,则可能需要大量分区。

我尝试了TMichel方法,但不适用于我。当我执行最大聚合时,我没有返回列表的最高值。因此,对我起作用的是:

def max_n_values(df, key, col_name, number):
    '''
    Returns the max n values of a spark dataframe
    partitioned by the key and ranked by the col_name
    '''
    w2 = Window.partitionBy(key).orderBy(f.col(col_name).desc())
    output = df.select('*',
                       f.row_number().over(w2).alias('rank')).filter(
                           f.col('rank') <= number).drop('rank')
    return output

def col_list(df, key, col_to_collect, name, score):
    w = Window.partitionBy(key).orderBy(f.col(score).desc())

    list_df = df.withColumn(name, f.collect_set(col_to_collect).over(w))
    size_df = list_df.withColumn('size', f.size(name))
    output = max_n_values(df=size_df,
                               key=key,
                               col_name='size',
                               number=1)
    return output
def最大值(df、键、列名称、编号):
'''
返回spark数据帧的最大n值
按键分区,按列名排序
'''
w2=Window.partitionBy(key).orderBy(f.col(col_name).desc())
输出=df。选择('*',将,
f、 行号()位于(w2)上方。别名('rank')。筛选器(

f、 col('rank')从Spark 2.4开始,在@mtoto的答案中创建的collect_列表(ArrayType)可以通过使用SparkSQL的内置函数和(无需udf)进行后处理:

注意:如果需要降序,请将
数组排序(值列表)
更改为
排序数组(值列表,False)


注意事项:array\u sort()和sort\u array()如果项目(在collect\u列表中)必须按混合顺序按多个字段(列)排序,即
orderBy('col1',desc('col2')排序将不起作用

您可以使用sort\u数组功能。如果您将日期和值收集为一个列表,则可以使用sorry\u数组对结果列进行排序,并仅保留所需的列

import operator
import pyspark.sql.functions as F

grouped_df = input_df.groupby("id") \
               .agg(F.sort_array(F.collect_list(F.struct("date", "value"))) \
.alias("collected_list")) \
.withColumn("sorted_list",col("collected_list.value")) \
.drop("collected_list")
.show(truncate=False)

+---+----------------+
|id |sorted_list     |
+---+----------------+
|1  |[10, 5, 15, 20] |
|2  |[100, 500, 1500]|
+---+----------------+ ```````

如果您想使用spark sql,以下是实现此目的的方法。假设表名(或临时视图)为
temp\u table

select
t1.id,
collect_list(value) as value_list
(Select * from temp_table order by id,date) t1
group by 1

在Spark SQL世界中,答案是:

SELECT 
browser, max(list)
from (
  SELECT
    id,
    COLLECT_LIST(value) OVER (PARTITION BY id ORDER BY date DESC) as list
  FROM browser_count
  GROUP BYid, value, date) 
Group by browser;

感谢您提供的详细示例…我刚刚在数百万的更大数据上进行了尝试,我得到了与collect_list完全相同的序列…有没有办法解释为什么会发生这种情况?另外,检查collect_list似乎只会在一个日期内将具有多个值的案例搞糟…这是否意味着collect_list也会维护顺序是什么?在代码中,在collect_list()之前对整个数据集进行排序所以是的。但这不是必须的,在收集了列表中的日期和值之后,对结果元组列表进行排序更有效。只是为了澄清……对列进行排序并在排序后的列上使用collect_list将保留顺序?在分布式系统中,顺序通常没有意义,因此除非每个id的LUE都在一个分区中。这个答案现在已经很旧了,我认为随着其他答案所描述的
array\u sort
的引入,这是最好的方法,因为它不需要UDF的开销。分步分组发生在排序之后。排序顺序会在分步分组中保留吗?没有这样的保证ntee AFAIKT这应该是公认的答案,因为使用了Spark基本函数-非常好!需要最大值,因为对于相同的“id”,将按排序顺序为每行创建一个列表:[10],然后是[10,5],然后是[10,5,15],然后是[10,5,15,20],对于id=1。获取列表的最大值需要最长的一个(此处为[10,5,15,20]).这对记忆的影响是什么?当我们处理10亿多个事件的链接时,当一个链可以在收集的列表中包含多达10000个项目时,这种方法比公认的答案更好吗?这不是扩展性的吗?如果
def max_n_values(df, key, col_name, number):
    '''
    Returns the max n values of a spark dataframe
    partitioned by the key and ranked by the col_name
    '''
    w2 = Window.partitionBy(key).orderBy(f.col(col_name).desc())
    output = df.select('*',
                       f.row_number().over(w2).alias('rank')).filter(
                           f.col('rank') <= number).drop('rank')
    return output

def col_list(df, key, col_to_collect, name, score):
    w = Window.partitionBy(key).orderBy(f.col(score).desc())

    list_df = df.withColumn(name, f.collect_set(col_to_collect).over(w))
    size_df = list_df.withColumn('size', f.size(name))
    output = max_n_values(df=size_df,
                               key=key,
                               col_name='size',
                               number=1)
    return output
from pyspark.sql.functions import collect_list, expr, struct

df.groupby('id') \
  .agg(collect_list(struct('date','value')).alias('value_list')) \
  .withColumn('value_list', expr('transform(array_sort(value_list), x -> x.value)')) \
  .show()
+---+----------------+
| id|      value_list|
+---+----------------+
|  1| [10, 5, 15, 20]|
|  2|[100, 500, 1500]|
+---+----------------+ 
import operator
import pyspark.sql.functions as F

grouped_df = input_df.groupby("id") \
               .agg(F.sort_array(F.collect_list(F.struct("date", "value"))) \
.alias("collected_list")) \
.withColumn("sorted_list",col("collected_list.value")) \
.drop("collected_list")
.show(truncate=False)

+---+----------------+
|id |sorted_list     |
+---+----------------+
|1  |[10, 5, 15, 20] |
|2  |[100, 500, 1500]|
+---+----------------+ ```````
select
t1.id,
collect_list(value) as value_list
(Select * from temp_table order by id,date) t1
group by 1
SELECT 
browser, max(list)
from (
  SELECT
    id,
    COLLECT_LIST(value) OVER (PARTITION BY id ORDER BY date DESC) as list
  FROM browser_count
  GROUP BYid, value, date) 
Group by browser;