Как разбить строки на разные столбцы в Spark DataFrame / DataSet?

Предположим, у меня есть набор данных вроде:

Name | Subject | Y1  | Y2 
A    | math    | 1998| 2000
B    |         | 1996| 1999
     | science | 2004| 2005

Я хочу разбить строки этого набора данных так, чтобы столбец Y2 был удален, например:

Name | Subject | Y1
A    | math    | 1998
A    | math    | 1999
A    | math    | 2000
B    |         | 1996
B    |         | 1997
B    |         | 1998
B    |         | 1999
     | science | 2004
     | science | 2005

Кто-нибудь может здесь что-то подсказать? Надеюсь, я прояснил свой вопрос. Заранее спасибо.


person neha    schedule 14.11.2016    source источник
comment
@cheseaux, почему ты удалил свой ответ? Мне это показалось верным.   -  person eliasah    schedule 14.11.2016


Ответы (4)


Я думаю, вам нужно всего лишь создать udf, чтобы создать диапазон. Затем вы можете использовать разнесение для создания необходимых строк:

val createRange = udf { (yearFrom: Int, yearTo: Int) =>
    (yearFrom to yearTo).toList
}

df.select($"Name", $"Subject", functions.explode(createRange($"Y1", $"Y2"))).show()

РЕДАКТИРОВАТЬ: версия этого кода для Python будет примерно так:

from pyspark.sql import Row
from pyspark.sql.functions import udf, explode
from pyspark.sql.types import IntegerType

createRange=udf( lambda (yearFrom, yearTo): list(range(yearFrom, yearTo)), IntegerType())

df.select($"Name", $"Subject", explode(createRange($"Y1", $"Y2"))).show()
person Carlos Vilchez    schedule 14.11.2016
comment
Можем ли мы сделать это с помощью python pandas? Я не могу понять ваш искровой код. - person neha; 14.11.2016

Я протестировал этот код в pyspark, и он работает должным образом:

data= sc.parallelize([["A","math",1998,2000],["B","",1996,1999],["","science",2004,2005]]

data.map(lambda reg: ((reg[0],reg[1]),(range(reg[2],reg[3]+1))) )
    .flatMapValues(lambda reg: reg).collect()

Более подробно, вам нужно преобразовать входные данные в пару RDD в форме (ключ, значение), где ключ состоит из первых двух полей, так как результат будет сглаженным, сохраняя ключ неизменным с flatMapValues. Отображаемые значения строятся как range от Y1 до Y2. Все это делается в первом map.

flatMapValues вернет каждое из значений range, связанных с его key.

Результат выглядит так:

[(('A', 'math'), 1998),
 (('A', 'math'), 1999),
 (('A', 'math'), 2000),
 (('B', ''), 1996),
 (('B', ''), 1997),
 (('B', ''), 1998),
 (('B', ''), 1999),
 (('', 'science'), 2004),
 (('', 'science'), 2005)]
person Manu Valdés    schedule 14.11.2016

Вот как это можно реализовать:

  val resultantDF= df.rdd.flatMap{row =>
    val rangeInitial = row.getInt(2)
    val rangeEnd = row.getInt(3)
    val array = rangeInitial to rangeEnd
    (List.fill(array.size)(row.getString(0)),List.fill(array.size)(row.getString(1)),array).zipped.toList
    }.toDF("Name","Subject","Y1")

resultantDF.show()
person Shiv4nsh    schedule 14.11.2016

Вы можете легко использовать искровой выбор, чтобы получить то, что вы хотите, во фрейме данных или даже в RDD.

Dataset<Row> sqlDF = spark.sql("SELECT Name,Subject,Y1 FROM tableName");

если вы начинаете с уже существующего фрейма данных, скажем, пользователей, вы можете использовать что-то вроде этого:

resultDF = usersDF.select("Name","Subject","Y1");
person Hasson    schedule 14.11.2016
comment
Смотрите вывод. Основная цель - разделить строки столько раз, сколько (Y2-Y1), а не просто удалить Y2. - person neha; 14.11.2016
comment
я хочу разбить строки на основе диапазона года, например, если данные похожи на «A | математика | 1998 | 2000`, то результат будет похож на A | math| 1998 A | math | 1999 A | math| 2000 - person neha; 14.11.2016
comment
import org.apache.spark.sql.functions._ val toRange = udf { (y1: Int, y2: Int) => (y1 to y2).toArray } input .withColumn("years", toRange($"Y1", $"Y2")) .select($"Name", explode($"years") as "Year") Но он показывает NumberFormatException: null из-за наличия нулевых значений. - person neha; 14.11.2016