Разделить строку в столбце искровых данных по группам захвата регулярных выражений - PullRequest
0 голосов
/ 31 октября 2018

Учитывая приведенный ниже фрейм данных, я хотел разбить столбец чисел на массив из 3 символов на элемент исходного числа в массиве

Заданный фрейм данных:

+---+------------------+
| id|           numbers|
+---+------------------+
|742|         000000000|
|744|            000000|
|746|003000000000000000|
+---+------------------+

Ожидаемый фрейм данных:

+---+----------------------------------+
| id|           numbers                |
+---+----------------------------------+
|742| [000, 000, 000]                  |
|744| [000, 000]                       |
|746| [003, 000, 000, 000, 000, 000]   |
+---+----------------------------------+

Я пробовал разные регулярные выражения при использовании функции split, приведенной ниже с регулярным выражением, которое, по моему мнению, должно было сработать с самой первой попытки:

import pyspark.sql.functions as f

df = spark.createDataFrame(
    [
        [742, '000000000'], 
        [744, '000000'], 
        [746, '003000000000000000'], 
    ],
    ["id", "numbers"]
)

df = df.withColumn("numbers", f.split("numbers", "[0-9]{3}"))

df.show()

Результат, однако, равен

+---+--------------+
| id|       numbers|
+---+--------------+
|742|      [, , , ]|
|744|        [, , ]|
|746|[, , , , , , ]|
+---+--------------+

Я хочу понять, что я делаю неправильно. Есть ли возможность установить глобальный флаг для получения всех совпадений или я что-то пропустил в регулярном выражении?

Ответы [ 3 ]

0 голосов
/ 31 октября 2018

Вот как вы можете сделать это без использования udf:

df = df.withColumn(
    "numbers",
    f.split(f.regexp_replace("numbers", "([0-9]{3})(?!$)", r"$1,"), ",")
)

df.show(truncate=False)
#+---+------------------------------+
#|id |numbers                       |
#+---+------------------------------+
#|742|[000, 000, 000]               |
#|744|[000, 000]                    |
#|746|[003, 000, 000, 000, 000, 000]|
#+---+------------------------------+

Сначала используйте pyspark.sql.functions.regexp_replace, чтобы заменить последовательности из 3 цифр последовательностью, за которой следует запятая. Затем разделите полученную строку запятой.

Шаблон замены "$1," означает первую группу захвата, за которой следует запятая.

В шаблоне сопоставления мы также добавляем отрицательный прогноз конца строки, (?!$), чтобы избежать добавления запятой в конец строки.

Ссылка: REGEXP_REPLACE группы захвата

0 голосов
/ 01 ноября 2018

Оба способа @pault и @Psidom великолепны! Вот еще одна альтернатива;

>>> split_udf = F.udf(lambda x: ','.join([''.join(i) for i in zip(*[iter(x)]*3)]))
>>> df.withColumn('numbers', F.split(split_udf('numbers'),',')).show(truncate=False)
+---+------------------------------+
|id |numbers                       |
+---+------------------------------+
|742|[000, 000, 000]               |
|744|[000, 000]                    |
|746|[003, 000, 000, 000, 000, 000]|
+---+------------------------------+
0 голосов
/ 31 октября 2018

split удалит шаблон, на который разбита строка; Для этого вам нужно создать udf:

from pyspark.sql.functions import udf
from pyspark.sql.types import ArrayType, StringType
import re

# create a udf with re.findall
split_by_three = f.udf(lambda s: re.findall(r'\d{3}', s), ArrayType(StringType()))
df.withColumn('numbers', split_by_three('numbers')).show(3, False)

#+---+------------------------------+
#|id |numbers                       |
#+---+------------------------------+
#|742|[000, 000, 000]               |
#|744|[000, 000]                    |
#|746|[003, 000, 000, 000, 000, 000]|
#+---+------------------------------+

df.withColumn('numbers', split_by_three('numbers')).printSchema()
#root
# |-- id: long (nullable = true)
# |-- numbers: array (nullable = true)
# |    |-- element: string (containsNull = true)
...