Spark: udf для получения dirname из path

#scala #apache-spark

#scala #apache-spark

Вопрос:

У меня есть безумное количество столбцов пути, которые мне нужно разделить на 2 столбца: basename и dirname. Я знаю, как легко получить базовое имя моих путей, используя :

 val df = Seq("/test/coucou/jambon/hello/file"
    ,"/test/jambon/test")
    .toDF("column1")
df.withColumn("basename", substring_index($"column1"  , "/", -1))
.show(2, false)
 ------------------------------ --------- 
|column1                       |basename |
 ------------------------------ --------- 
|/test/coucou/jambon/hello/file|file     |
|/test/jambon/test             |test     |
 ------------------------------ --------- 
  

Однако я изо всех сил пытаюсь получить dirname следующим образом :

  ------------------------------ -------------------------- 
|column1                       |dirname                   |
 ------------------------------ -------------------------- 
|/test/coucou/jambon/hello/file|/test/coucou/jambon/hello |
|/test/jambon/test             |/test/jambon              |
 ------------------------------ -------------------------- 
  

Я пробовал различные решения, однако я не могу найти функциональное столбчатое решение.
Моей лучшей идеей было бы вычесть $"basename" из $"column1" , однако я не смог найти способ вычесть строку в Spark.

Ответ №1:

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

 //Creating Test Data
val df = Seq("/test/coucou/jambon/hello/file"
  ,"/test/jambon/prout/test")
  .toDF("column1")

val test = df.withColumn("basename", substring_index($"column1"  , "/", -1))
    .withColumn("path", expr("substring(column1, 1, length(column1)-length(basename)-1)"))

test.show(false)
 ------------------------------ -------- ------------------------- 
|column1                       |basename|path                     |
 ------------------------------ -------- ------------------------- 
|/test/coucou/jambon/hello/file|file    |/test/coucou/jambon/hello|
|/test/jambon/prout/test       |test    |/test/jambon/prout       |
 ------------------------------ -------- ------------------------- 
  

Комментарии:

1. выглядит интересно, хотя я хотел бы, чтобы они были более простым способом: D Для IMO это слишком сложно, что должно быть намного проще…. Большое вам спасибо за вашу помощь.

2. Ну, после того, как в нем появилось больше мыслей. кажется, это единственный способ обработать короткий путь, такой как /filex.txt сохранение последнего / (удалите -1 в подстроке)

Ответ №2:

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

Получите правильное регулярное выражение. regexp_extract UDF даст вам то, что вы хотели.

    val df = Seq("/test/coucou/jambon/hello/file"
      , "/test/jambon/prout/test")
      .toDF("column1")

    import org.apache.spark.sql.functions.regexp_extract

    df.withColumn("path", regexp_extract('column1, "^\/(\w \/) ", 0)).withColumn("fileName",regexp_extract('column1, "\w $", 0)).show(false)
  

Вывод

  ------------------------------ -------------------------- -------- 
|column1                       |path                      |fileName|
 ------------------------------ -------------------------- -------- 
|/test/coucou/jambon/hello/file|/test/coucou/jambon/hello/|file    |
|/test/jambon/prout/test       |/test/jambon/prout/       |test    |
 ------------------------------ -------------------------- -------- 
  

Редактировать:
Регулярным выражением без косой черты в конце легче управлять:

 df.withColumn("path",regexp_extract($"column1", "^(. )(/. )$", 1 ) ) )
  

Комментарии:

1. не работает при использовании более короткого пути, такого как: /file.txt путь к столбцу пуст и должен быть IMO a /

Ответ №3:

Другим подходом было бы использование UDFS:

 import org.apache.spark.sql.functions.udf

val pathUDF = udf((s: String) => s.substring(0, s.lastIndexOf("/")))

val test = df.withColumn("basename", substring_index($"column1"  , "/", -1))
    .withColumn("path", pathUDF($"column1"))

test.show(false)
 ------------------------------ -------- ------------------------- 
|column1                       |basename|path                     |
 ------------------------------ -------- ------------------------- 
|/test/coucou/jambon/hello/file|file    |/test/coucou/jambon/hello|
|/test/jambon/prout/test       |test    |/test/jambon/prout       |
 ------------------------------ -------- ------------------------- 
  

Комментарии:

1. не работает при использовании более короткого пути, такого как: /file.txt путь к столбцу пуст и должен быть IMO a /