Я знаю, что мы можем заменить значения в столбце фрейма данных и вернуть новый фрейм данных с обновленными значениями, используя метод ниже:
dataframe.withColumn("col1",when(col("col1").equalTo("this"),"that").otherwise(col("make")))
но это изменит все значение столбца там, где это необходимо.
Теперь у меня есть немного сложный фрейм данных:
| colleagues| name|
|[guy1, guy2, guy3]|Thisguy|
|[guy4, guy5, guy6]|Thatguy|
|[guy7, guy8, guy9]|Someguy|
Здесь у меня есть столбец «коллеги», в котором хранятся массивы. И я хочу заменить определенный элемент любого массива, например, вместо 'guy2' в первой строке я хочу 'guy10' в моем новом фрейме данных Как я могу этого добиться? Пожалуйста помоги.





Прежде чем предлагать окончательное решение, необходимо ответить на несколько вопросов (например, порядок элементов в массиве colleagues после замены некоторых), но я не хочу тянуть это слишком долго. Давайте посмотрим на очень распространенный подход к решению подобных проблем.
Поскольку столбец colleagues является столбцом массива (и Spark очень эффективен при запросах по строкам), вы должны сначала его explode (или posexplode). С помощью строк на элемент массива вы можете внести необходимые изменения и, в конце концов, collect_list, чтобы вернуть столбец массива.
explode(e: Column): Column Creates a new row for each element in the given array or map column.
posexplode(e: Column): Column Creates a new row for each element with position in the given array or map column.
Давайте использовать следующий набор данных names:
val names = Seq((Array("guy1", "guy2", "guy3"), "Thisguy")).toDF("colleagues", "name")
scala> names.show
+------------------+-------+
| colleagues| name|
+------------------+-------+
|[guy1, guy2, guy3]|Thisguy|
+------------------+-------+
scala> names.printSchema
root
|-- colleagues: array (nullable = true)
| |-- element: string (containsNull = true)
|-- name: string (nullable = true)
Давайте explode, внесем изменения и в конце концов collect_list.
val elements = names.withColumn("elements", explode($"colleagues"))
scala> elements.show
+------------------+-------+--------+
| colleagues| name|elements|
+------------------+-------+--------+
|[guy1, guy2, guy3]|Thisguy| guy1|
|[guy1, guy2, guy3]|Thisguy| guy2|
|[guy1, guy2, guy3]|Thisguy| guy3|
+------------------+-------+--------+
Это то, с чем Spark SQL может легко справиться. Воспользуемся regexp_replace (Что? Regexp ?! А теперь две проблемы :)).
val replaced = elements.withColumn("replaced", regexp_replace($"elements", "guy2", "guy10"))
scala> replaced.show
+------------------+-------+--------+--------+
| colleagues| name|elements|replaced|
+------------------+-------+--------+--------+
|[guy1, guy2, guy3]|Thisguy| guy1| guy1|
|[guy1, guy2, guy3]|Thisguy| guy2| guy10|
|[guy1, guy2, guy3]|Thisguy| guy3| guy3|
+------------------+-------+--------+--------+
В конце сгруппируем по столбцу исходного массива и воспользуемся функцией группировки collect_list.
val solution = replaced
.groupBy($"colleagues" as "before")
.agg(
collect_list("replaced") as "after",
first("name") as "name")
scala> solution.show
+------------------+-------------------+-------+
| before| after| name|
+------------------+-------------------+-------+
|[guy1, guy2, guy3]|[guy1, guy10, guy3]|Thisguy|
+------------------+-------------------+-------+
В качестве альтернативы вы также можете написать настраиваемую определяемую пользователем функцию, но она не выиграет от такого количества оптимизаций, как решение выше, поэтому я бы не рекомендовал ее (и будет отображаться только по запросу).
Лучшим подходом было бы написать собственный логический оператор (LogicalPlan), который бы делал все это и участвовал в оптимизации, но избегал обменов (введенных groupBy). Однако это была бы довольно продвинутая разработка Spark, которой я еще не занимался.
неважно, я нашел способ: df selectExpr("*", "posexplode(s) as (p,c)") drop("s")
решения для withColumn (..., explode ()) ясны. А что насчет Posexplode? ваш issues.apache.org/jira/browse/SPARK-20174 пока не имеет обновлений.