web-dev-qa-db-fra.com

Divisez 1 colonne en 3 colonnes dans spark scala

J'ai un dataframe dans Spark en utilisant scala qui a une colonne que j'ai besoin de fractionner.

scala> test.show
+-------------+
|columnToSplit|
+-------------+
|        a.b.c|
|        d.e.f|
+-------------+

J'ai besoin de cette colonne divisée pour ressembler à ceci:

+--------------+
|col1|col2|col3|
|   a|   b|   c|
|   d|   e|   f|
+--------------+

J'utilise Spark 2.0.0

Merci

28
Matt Maurer

Essayer:

df.withColumn("_tmp", split($"columnToSplit", "\\.")).select(
  $"_tmp".getItem(0).as("col1"),
  $"_tmp".getItem(1).as("col2"),
  $"_tmp".getItem(2).as("col3")
).drop("_tmp")
60
user6022341

Pour faire cela par programme, vous pouvez créer une séquence d'expressions avec (0 until 3).map(i => col("temp").getItem(i).as(s"col$i")) (supposons que vous ayez besoin de 3 colonnes) puis appliquez-le à select avec : _* syntaxe:

df.withColumn("temp", split(col("columnToSplit"), "\\.")).select(
    (0 until 3).map(i => col("temp").getItem(i).as(s"col$i")): _*
).show
+----+----+----+
|col0|col1|col2|
+----+----+----+
|   a|   b|   c|
|   d|   e|   f|
+----+----+----+

Pour conserver toutes les colonnes:

df.withColumn("temp", split(col("columnToSplit"), "\\.")).select(
    col("*") +: (0 until 3).map(i => col("temp").getItem(i).as(s"col$i")): _*
).show
+-------------+---------+----+----+----+
|columnToSplit|     temp|col0|col1|col2|
+-------------+---------+----+----+----+
|        a.b.c|[a, b, c]|   a|   b|   c|
|        d.e.f|[d, e, f]|   d|   e|   f|
+-------------+---------+----+----+----+

Si vous utilisez pyspark, utilisez une liste de compréhension pour remplacer le map dans scala:

df = spark.createDataFrame([['a.b.c'], ['d.e.f']], ['columnToSplit'])
from pyspark.sql.functions import col, split

(df.withColumn('temp', split('columnToSplit', '\\.'))
   .select(*(col('temp').getItem(i).alias(f'col{i}') for i in range(3))
).show()
+----+----+----+
|col0|col1|col2|
+----+----+----+
|   a|   b|   c|
|   d|   e|   f|
+----+----+----+
25
Psidom

Une solution qui évite la partie sélectionnée. Ceci est utile lorsque vous souhaitez simplement ajouter les nouvelles colonnes:

case class Message(others: String, text: String)

val r1 = Message("foo1", "a.b.c")
val r2 = Message("foo2", "d.e.f")

val records = Seq(r1, r2)
val df = spark.createDataFrame(records)

df.withColumn("col1", split(col("text"), "\\.").getItem(0))
  .withColumn("col2", split(col("text"), "\\.").getItem(1))
  .withColumn("col3", split(col("text"), "\\.").getItem(2))
  .show(false)

+------+-----+----+----+----+
|others|text |col1|col2|col3|
+------+-----+----+----+----+
|foo1  |a.b.c|a   |b   |c   |
|foo2  |d.e.f|d   |e   |f   |
+------+-----+----+----+----+

Mise à jour: Je recommande vivement d'utiliser implémentation de Psidom pour éviter de se scinder trois fois.

17
Sascha Vetter

Cela ajoute des colonnes au DataFrame d'origine et n'utilise pas select, et ne divise qu'une seule fois à l'aide d'une colonne temporaire:

import spark.implicits._

df.withColumn("_tmp", split($"columnToSplit", "\\."))
  .withColumn("col1", $"_tmp".getItem(0))
  .withColumn("col2", $"_tmp".getItem(1))
  .withColumn("col3", $"_tmp".getItem(2))
  .drop("_tmp")
5
soaptree

Cela développe la réponse de Psidom et montre comment effectuer le fractionnement de manière dynamique, sans coder en dur le nombre de colonnes. Cette réponse lance une requête pour calculer le nombre de colonnes.

val df = Seq(
  "a.b.c",
  "d.e.f"
).toDF("my_str")
.withColumn("letters", split(col("my_str"), "\\."))

val numCols = df
  .withColumn("letters_size", size($"letters"))
  .agg(max($"letters_size"))
  .head()
  .getInt(0)

df
  .select(
    (0 until numCols).map(i => $"letters".getItem(i).as(s"col$i")): _*
  )
  .show()
1
Powers