【问题标题】:Convert Column of List to Dataframe将列表的列转换为数据框
【发布时间】:2017-12-08 09:49:46
【问题描述】:

我在 spark 数据框中有一列列表。

+-----------------+
|features         |
+-----------------+
|[0,45,63,0,0,0,0]|
|[0,0,0,85,0,69,0]|
|[0,89,56,0,0,0,0]|
+-----------------+

如何将其转换为 spark 数据框,其中列表中的每个元素都是数据框中的一列?我们可以假设列表的大小相同。

例如,

+--------------------+
|c1|c2|c3|c4|c5|c6|c7|
+--------------------+
|0 |45|63|0 |0 |0 |0 |
|0 |0 |0 |85|0 |69|0 |
|0 |89|56|0 |0 |0 |0 |
+--------------------+

【问题讨论】:

  • 可能类似于this?
  • 特征列的数据类型是什么。你能发布你的架构吗?

标签: pyspark apache-spark-sql spark-dataframe pyspark-sql


【解决方案1】:

你描述的实际上是VectorAssembler操作的反转。

可以通过转换为中间RDD来实现,如下:

spark.version
# u'2.2.0'

# your data:
df.show(truncate=False)
# +-----------------+ 
# |        features | 
# +-----------------+
# |[0,45,63,0,0,0,0]|
# |[0,0,0,85,0,69,0]|
# |[0,89,56,0,0,0,0]|
# +-----------------+ 

dimensionality = 7
out = df.rdd.map(lambda x: [float(x[0][i]) for i in range(dimensionality)]).toDF(schema=['c'+str(i+1) for i in range(dimensionality)])
out.show()
# +---+----+----+----+---+----+---+ 
# | c1|  c2|  c3|  c4| c5|  c6| c7|
# +---+----+----+----+---+----+---+ 
# |0.0|45.0|63.0| 0.0|0.0| 0.0|0.0|
# |0.0| 0.0| 0.0|85.0|0.0|69.0|0.0| 
# |0.0|89.0|56.0| 0.0|0.0| 0.0|0.0| 
# +---+----+----+----+---+----+---+

【讨论】:

    【解决方案2】:

    你可以使用getItem:

    df.withColumn("c1", df["features"].getItem(0))\
      .withColumn("c2", df["features"].getItem(1))\
      .withColumn("c3", df["features"].getItem(2))\
      .withColumn("c4", df["features"].getItem(3))\
      .withColumn("c5", df["features"].getItem(4))\
      .withColumn("c6", df["features"].getItem(5))\
      .withColumn("c7", df["features"].getItem(6))\
      .drop('features').show()
    
    +--------------------+
    |c1|c2|c3|c4|c5|c6|c7|
    +--------------------+
    |0 |45|63|0 |0 |0 |0 |
    |0 |0 |0 |85|0 |69|0 |
    |0 |89|56|0 |0 |0 |0 |
    +--------------------+
    

    【讨论】:

      【解决方案3】:

      这是一个不转换为rdd的替代方案,

      from pyspark.sql import functions as F
      
      ##Not incase of vectorAssembeler.
      stop = df.select(F.max(F.size('features')).alias('size')).first().size ## if having a list of varying size, this might be useful.
      
      udf1 = F.udf(lambda x : x.toArray().tolist(),ArrayType(FloatType()))
      df = df.withColumn('features1',udf1('features'))
      
      df.select(*[df.features1[i].alias('col_{}'.format(i)) for i in range(1,stop)]).show()
      +-----+-----+-----+-----+-----+-----+
      |col_1|col_2|col_3|col_4|col_5|col_6|
      +-----+-----+-----+-----+-----+-----+
      |   45|   63|    0|    0|    0|    0|
      |    0|    0|   85|    0|   69|    0|
      +-----+-----+-----+-----+-----+-----+
      

      【讨论】:

      • 该问题指定了“列表”列。为什么在这里使用 toArray()?
      • 如果是列表列,没关系,我们不需要udf本身。但是,列名“功能”是它击中我的地方。
      • @desertnaut 我同意
      • @desertnaut 也同意。
      • 好。作为奖励,我在您的帖子中添加了代码突出显示... ;) @mayankagrawal
      【解决方案4】:

      @desertnaut 的回答也可以用 dataframe 和 udf 来完成。

      import pyspark.sql.functions as F
      
      dimensionality = 7
      column_names = ['c'+str(i+1) for i in range(dimensionality)]
      splits = [F.udf(lambda val:val[i],FloatType()) for i in range(dimensionality)]
      df = df.select(*[s('features').alias(j) for s,j in zip(splits,column_names)])
      

      【讨论】:

      • 这是否适用于向量列或数组类型列?
      • @Suresh 不错 - 它不适用于向量列(已测试)
      • 没错,如果我们使用数组类型,我们可以直接索引它。
      • @mayankagrawal 没有
      • @mayankagrawal 类向量具有 toArray() 方法。只有类 densevector 和 sparsevector 有值。如果我错了,请纠正我。请检查这个,spark.apache.org/docs/2.2.0/api/python/…
      猜你喜欢
      • 2020-04-17
      • 2022-11-14
      • 2021-03-22
      • 2015-11-14
      相关资源
      最近更新 更多