Разбор фиксированной длины в искровых скалах - PullRequest
1 голос
/ 30 июня 2019

Я создал фрейм данных, и ввод выглядит так:

   +-----------------------------------+
   |value                              |
   +-----------------------------------+
   |1   PRE123                    21   |
   |2   TEST                      32   |
   |7   XYZ                       .7   |
   +-----------------------------------+

и на основе приведенной ниже информации метаданных нам нужно разделить вышеуказанный фрейм данных и создать новый фрейм данных с именами столбцов id, name и class, и в этих метаданных json даны начало и индексная привязка.

   {
    "columnName": "id",
    "start": 1,
    "end": 2
  },
  {
    "columnName": "name",
    "start": 5,
    "end": 10
  },
  {
    "columnName": "class",
    "start": 20,
    "end": 22
  }

ВЫХОД:

  +---+------+-----+
  | id|  name|class|
  +---+------+-----+
  |  1|PRE123|   21|
  |  2|  TEST|   32|
  |  7|   XYZ|   .7|
  +---+------+-----+

Для загрузки df я создал список:

   list.+=(loadedDF.col("value").substr(fixedLength.getStart, (fixedLength.getEnd - fixedLength.getStart)).alias(fixedLength.getColumnName))

и из этого списка я создал фрейм данных

var df: DataFrame = loadedDF.select(list: _*)

Нужно знать порядок лучшего подхода для создания кадра данных из метаданных. Поскольку созданный список доставит все данные на узел драйвера.

1 Ответ

1 голос
/ 30 июня 2019

Если я правильно понял ваши требования, вы пытаетесь извлечь столбцы из строки, разделенной произвольным числом пробелов.

Вот одно решение с функцией substr:

val df = Seq(
  ("1   PRE123         21"),
  ("2   TEST           32"),
  ("7   XYZ            .7"))
.toDF("value")

val colMetadata = Map("id" -> (1,2), "name" -> (5,10), "class" -> (20,22))

val columns = colMetadata.map { case (cname, meta) => 
  val len = meta._2 - meta._1   
  $"value".substr(meta._1, len).as(cname)
}.toSeq

df.select(columns:_*).show

И общее решение, когда у вас нет доступных границ столбцов с использованием функции разделения:

import org.apache.spark.sql.functions.split

val df = Seq(
  ("1   PRE123         21"),
  ("2   TEST           32"),
  ("7   XYZ            .7"))
.toDF("value")

val colNames = Seq("id", "name", "class")

val columns = colNames.zipWithIndex.map { case (cname, idx) =>
      split($"value", "\\s+").getItem(idx).as(cname)
}

df.select(columns:_*).show

Вывод:

+---+------+-----+
| id|  name|class|
+---+------+-----+
|  1|PRE123|   21|
|  2|  TEST|   32|
|  7|   XYZ|   .7|
+---+------+-----+

Обратите внимание, что я использовал \\s+ в качестве разделителя.Это представляет регулярное выражение для одного или нескольких пробелов .

...