Python 计算单个列中列表中的值的分期

Python 计算单个列中列表中的值的分期,python,apache-spark,pyspark,counter,Python,Apache Spark,Pyspark,Counter,我有一个PySpark数据帧,其中1列由字符串列表组成。我想计算所有行中每个字符串列表中每个元素的实例数。伪代码: counter = Counter() for attr_list in df['attr_list']: counter.update(attr_list) 另一种方法是连接所有行中的所有列表,并从单个巨大列表中构建一个计数器。在PySpark中有没有一种有效的方法可以做到这一点 正确的输出将是一个集合。Counter()对象,该对象填充了所有列中所有列表中每个项目的出现

我有一个PySpark数据帧,其中1列由字符串列表组成。我想计算所有行中每个字符串列表中每个元素的实例数。伪代码:

counter = Counter()
for attr_list in df['attr_list']:
   counter.update(attr_list)
另一种方法是连接所有行中的所有列表,并从单个巨大列表中构建一个计数器。在PySpark中有没有一种有效的方法可以做到这一点


正确的输出将是一个集合。Counter()对象,该对象填充了所有列中所有列表中每个项目的出现次数,即,如果对于给定列,第1行具有列表
['a','b','c']
,第2行具有列表
['b','c','d']
,我们将得到一个类似
{'a':1,'b':2,'c':2,'d':1}

您可以尝试使用rdd的
distinct
flatMap
方法,为此只需将列转换为and rdd并执行这些操作

counter = (df
           .select("attr_list")
           .rdd
           # join all strings in the list and then split to get each word
           .map(lambda x: " ".join(x).split(" ")) 
           .flatMap(lambda x: x)
           # make a tuple for each word so later it can be grouped by to get its frequency count
           .map(lambda x: (x, 1))
           .reduceByKey(lambda a,b: a+b)
           .collectAsMap())

转换为RDD的一个选项是将所有数组合并为一个数组,然后在其上使用
计数器
对象

from collections import Counter
all_lists = df.select('listCol').rdd
print(Counter(all_lists.map(lambda x: [i for i in x[0]]).reduce(lambda x,y: x+y)))
另一个选项是使用
分解
分组方式
并将结果合并到
字典

from pyspark.sql.functions import explode
explode_df = df.withColumn('exploded_list',explode(df.listCol))
counts = explode_df.groupBy('exploded_list').count()
counts_tuple = counts.rdd.reduce(lambda a,b : a+b)
print({counts_tuple[i]:counts_tuple[i+1] for i in range(0,len(counts_tuple)-1,2)})

如果您知道必须计数的
元素,那么您可以将其与
spark2.4+一起使用。
而且速度会非常快。(使用
高阶函数过滤器
结构

Out:{'a':1,'b':3,'c':2,'d':1}

第二种方法,使用变换、聚合、分解和分组(不需要指定元素):


你的spark版本是什么?这回答了你的问题吗?Thx。不幸的是,在这种情况下有3000多个元素,这就是为什么我希望使用类似于python计数器()的东西dict.使用哈希表来增加仅使用给定行中的列表元素访问的变量应该比对每个元素迭代一次快得多。我不同意,上面的代码不是迭代的,它是在后台立即发生的,并且没有使用类似counter()的快速方法dict in spark.您是否尝试过将所有3000个不同的元素按上述方式放入元素列表并运行代码?嗨,Mohammad,谢谢您的解决方案。我得到了一些非常有趣的结果。我只是通过执行rdd.collect()来实现这一点在属性列上,然后在列表中逐个更新计数器dict。执行此操作时,实际上是rdd.collect()花费的时间最多。您的解决方案速度要快得多,但是这两种解决方案的扩展方式非常不同。我测试了10k样本和50k样本。使用计数器dict和collect()的方法每个样本分别用了700秒和713秒。你的方法分别用了31秒和48秒。谢谢!@theShmoo没问题,谢谢你的反馈,因为我对速度很好奇。如果你有更大的数据,速度上的差异也会更大
df.show()

#+------------+
#|    atr_list|
#+------------+
#|[a, b, b, c]|
#|   [b, c, d]|
#+------------+

elements=['a','b','c','d']

from pyspark.sql import functions as F
collected=df.withColumn("struct", F.struct(*[(F.struct(F.expr("size(filter(atr_list,x->x={}))"\
                                                    .format("'"+y+"'"))).alias(y)) for y in elements]))\
            .select(*[F.sum(F.col("struct.{}.col1".format(x))).alias(x) for x in elements])\
            .collect()[0]

{elements[i]: [x for x in collected][i] for i in range(len(elements))} 
from pyspark.sql import functions as F

a=df.withColumn("atr", F.expr("""transform(array_distinct(atr_list),x->aggregate(atr_list,0,(acc,y)->\
                               IF(y=x, acc+1,acc)))"""))\
  .withColumn("zip", F.explode(F.arrays_zip(F.array_distinct("atr_list"),("atr"))))\
  .select("zip.*").withColumnRenamed("0","elements")\
  .groupBy("elements").agg(F.sum("atr").alias("sum"))\
  .collect()

{a[i][0]: a[i][1] for i in range(len(a))}