Хобрук: Ваш путь к мастерству в программировании

Как анализировать поля с разделителями, если некоторые (под)поля пусты?

Я использую Spark 2.1.1 и Scala 2.11.8 в искровой оболочке.

Мой набор входных данных выглядит примерно так:

2017-06-18 00:00:00 , 1497769200 , z287570731_serv80i:7:175 , 5:Re

2017-06-18 00:00:00 , 1497769200 , p286274731_serv80i:6:100 , 138 

2017-06-18 00:00:00 , 1497769200 , t219420679_serv37i:2:50 , 5

2017-06-18 00:00:00 , 1497769200 , v290380588_serv81i:12:800 , 144:Jo

2017-06-18 00:00:00 , 1497769200 , z292902510_serv83i:4:45 , 5:Re

2017-06-18 00:00:00 , 1497769200 , v205454093_serv75i:5:70 , 50:AK

Он сохраняется в виде файла CSV, который читается с помощью sc.textFile("input path").

После нескольких преобразований это вывод RDD, который у меня есть:

(String, String) = ("Re ",7)

Я получаю это, выполняя

val tid = read_file.map { line =>
  val arr = line.split(",")
  (arr(3).split(":")(1), arr(2).split(":")(1))
}

Мой ввод RDD:

( z287570731_serv80i:7:175 , 5:Re )

( p286274731_serv80i:6:100 , 138 )

( t219420679_serv37i:2:50 , 5 )

( v290380588_serv81i:12:800 , 144:Jo )

( z292902510_serv83i:4:45 , 5:Re )

Как можно заметить, в первом столбце записи 2 у меня есть

5:Re 

из которых я получаю вывод

("Re ",7)

Однако, когда я дохожу до второй строки, в соответствии с форматом столбец 2 равен 138, который должен быть

138:null 

но дает ArrayIndexOutOfBoundsException при выполнении

tid.collect()

Как я могу исправить это, чтобы нуль отображался со 138 и 5 для второй и третьей строк соответственно? Я пытался сделать это следующим образом:

tid.filter(x => x._1 != null )
27.06.2017

Ответы:


1

Если ваши данные, как показано ниже в файле

( z287570731_serv80i:7:175 , 5:Re )
( p286274731_serv80i:6:100 , 138 )
( t219420679_serv37i:2:50 , 5 )
( v290380588_serv81i:12:800 , 144:Jo )
( z292902510_serv83i:4:45 , 5:Re )

Затем вы можете использовать

val tid = sc.textFile("path to the input file")
  .map(line => line.split(","))
  .map(array => {
    if (array(1).contains(":")) (array(1).split(":")(1).replace(")", "").trim, array(0).split(":")(1))
    else (null, array(0).split(":")(1))
  })
tid.foreach(println)

который должен дать вам вывод как

(Re,7)
(null,6)
(null,2)
(Jo,12)
(Re,4)

Но если у вас есть данные как

2017-06-18 00:00:00 , 1497769200 , z287570731_serv80i:7:175 , 5:Re
2017-06-18 00:00:00 , 1497769200 , p286274731_serv80i:6:100 , 138
2017-06-18 00:00:00 , 1497769200 , t219420679_serv37i:2:50 , 5
2017-06-18 00:00:00 , 1497769200 , v290380588_serv81i:12:800 , 144:Jo
2017-06-18 00:00:00 , 1497769200 , z292902510_serv83i:4:45 , 5:Re
2017-06-18 00:00:00 , 1497769200 , v205454093_serv75i:5:70 , 50:AK
2017-06-18 00:00:00 , 1497769200 , z287096299_serv80i:19:15000 , 39:Re

Тогда вам нужно сделать

val tid = sc.textFile("path to the input file")
  .map(line => line.split(","))
  .map(array => {
    if (array(3).contains(":")) (array(3).split(":")(1).replace(")", "").trim, array(2).split(":")(1))
    else (null, array(2).split(":")(1))
  })
tid.foreach(println)

И у вас должен быть вывод как

(Re,7)
(null,6)
(null,2)
(Jo,12)
(Re,4)
(AK,5)
(Re,19)
27.06.2017
  • (null,00) (null,00) (null,00) (null,00) - это вывод, который я получаю 27.06.2017
  • Ваш ввод такой же, как определено? 27.06.2017
  • поделитесь данными образца входного файла. Может быть, я могу помочь. :) 27.06.2017
  • означает ли это, что ваш входной файл представляет собой одну строку? 27.06.2017

  • 2

    Проблема в том, что вы ожидаете как минимум две части в позиции, а у вас может быть только одна.

    Ниже приведена строка, которая вызывает проблему.

    {var arr = line.split(","); (arr(3).split(":")(1),arr(2).split(":")(1))});
    

    После того, как вы сделаете line.split(","), вы затем arr(3).split(":")(1), а также arr(2).split(":")(1).

    Конечно, слишком много предположений о формате, и его избили пропущенными значениями.

    но дает ArrayIndexOutOfBoundsException при выполнении

    Это потому, что вы обращаетесь к элементам 3 и 2, но имеете только 2 (!)

    scala> sc.textFile("input.csv").
      map { line => line.split(",").toSeq }.
      foreach(println)
    WrappedArray(( z287570731_serv80i:7:175i ,  5:Re ))
    WrappedArray(( p286274731_serv80i:6:100 ,  138 ))
    

    Проблема почти не связана со Spark. Это обычная проблема Scala, когда данные не там, где вы ожидаете.

    scala> val arr = "hello,world".split(",")
    arr: Array[String] = Array(hello, world)
    

    Обратите внимание, что то, что выше, — это просто чистый Scala.

    Решение 1 — RDD Spark Core

    Учитывая следующий набор данных...

    2017-06-18 00:00:00 , 1497769200 , z287570731_serv80i:7:175 , 5:Re
    2017-06-18 00:00:00 , 1497769200 , p286274731_serv80i:6:100 , 138 
    2017-06-18 00:00:00 , 1497769200 , t219420679_serv37i:2:50 , 5
    2017-06-18 00:00:00 , 1497769200 , v290380588_serv81i:12:800 , 144:Jo
    2017-06-18 00:00:00 , 1497769200 , z292902510_serv83i:4:45 , 5:Re
    2017-06-18 00:00:00 , 1497769200 , v205454093_serv75i:5:70 , 50:AK
    

    ... Я бы сделал следующее:

    val solution = sc.textFile("input.csv").
      map { line => line.split(",") }.
      map { case Array(_, _, third, fourth) => (third, fourth) }.
      map { case (third, fourth) =>
        val Array(_, a @ _*) = fourth.split(":")
        val Array(_, right, _) = third.split(":")
        (a.headOption.orNull, right)
      }
    scala> solution.foreach(println)
    (Re,7)
    (null,6)
    (Re,4)
    (null,2)
    (AK,5)
    (Jo,12)
    

    Решение 2 — DataFrames Spark SQL

    Я настоятельно рекомендую использовать Spark SQL для таких преобразований данных. Как вы сказали, вы новичок в Spark, так почему бы не начать с правильного места, а именно с Spark SQL.

    val solution = spark.
      read.
      csv("input.csv").
      select($"_c2" as "third", $"_c3" as "fourth").
      withColumn("a", split($"fourth", ":")).
      withColumn("left", $"a"(1)).
      withColumn("right", split($"third", ":")(1)).
      select("left", "right")
    scala> solution.show(false)
    +----+-----+
    |left|right|
    +----+-----+
    |Re  |7    |
    |null|6    |
    |null|2    |
    |Jo  |12   |
    |Re  |4    |
    |AK  |5    |
    +----+-----+
    
    27.06.2017
  • Я ценю совет, большое спасибо. Но я объясню всю проблему здесь. Мне нужно сопоставить значения в двух столбцах, скажем (Re, 5); (Нуль, 6) ; (Нуль, 5) ; (Джо, 12) и (Ре, 4). Такие места, как 138 и 5, в которых нет данных, таких как первая строка (5: Re), должны быть помечены как нулевые, а затем необходимо подсчитать количество вхождений Re, Jo, Null и т. д. Я не понимаю, как здесь помогает «если». 27.06.2017
  • Весь код должен выполняться в одной строке, верно? Также я думаю, что мой подход к изучению Spark и Scala до сих пор был неправильным. Не могли бы вы подсказать мне, как мне начать? 27.06.2017
  • Может быть, но не обязан. Я отформатировал его, чтобы вы могли скопировать и вставить его в spark-shell и сами посмотреть, как это работает. 27.06.2017
  • Я сделал. Я получил сообщение об ошибке, говорящее о том, что разделение значений не является членом массива [String] 27.06.2017
  • @JacekLaskowski Означает ли val Array(_, a @ _*) = rt.split(":") перемещение всех элементов массива rt.split(":") в a, кроме первого? 27.06.2017
  • Интересный. Похоже, у нас разные входные данные. Можете ли вы удалить последний map и посмотреть, что вы получите. 27.06.2017
  • @филантроверт да. 27.06.2017

  • 3

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

    val rdd = sc.parallelize(Array(
        ( "z287570731_serv80i:7:175" , "5:Re" ),
        ( "p286274731_serv80i:6:100" , "138" ),
        ( "t219420679_serv37i:2:50" , "5" ),
        ( "v290380588_serv81i:12:800" , "144:Jo" ),
        ( "z292902510_serv83i:4:45" , "5:Re" )))
    
    
    rdd.map { x =>
        val idx = x._2.lastIndexOf(":")
        if(idx == -1) (x._1, x._2+":null")
        else (x._1, x._2)
    }
    

    Очевидно, есть лучшие (меньшее количество строк кода) способы сделать то, что вы хотите сделать, но для новичка хорошо размещать каждый шаг в одной команде, чтобы она была легко читаемой и понятной, особенно с scala, где вы можете остановить глобальное потепление с помощью одна строка кода.

    27.06.2017
  • Почему я получаю значения _2 и _1, которые не являются членами строки при выполнении rdd.map? 27.06.2017
  • Потому что в этом примере я использовал Tuple2 для хранения полей. Поскольку вы используете split в строке, вы, вероятно, получите Array, поэтому вы можете попробовать использовать x(0) , x(1). Это зависит от того, как выглядит ваш RDD. x._1 для кортежей. x(0) для массивов. 27.06.2017
  • Новые материалы

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

    Как построить любой стол
    Я разработчик программного обеспечения. Я люблю делать вещи и всегда любил. Для меня программирование всегда было способом создавать вещи, используя только компьютер и мое воображение...

    Обзор: Машинное обучение: классификация
    Только что закончил третий курс курса 4 часть специализации по машинному обучению . Как и второй курс, он был посвящен низкоуровневой работе алгоритмов машинного обучения. Что касается..

    Разработка расширений Qlik Sense с qExt
    Использование современных инструментов веб-разработки для разработки крутых расширений Вы когда-нибудь хотели кнопку для установки переменной в приложении Qlik Sense? Когда-нибудь просили..