У меня есть фрейм данных, имеющий два столбца: «id» (int) и «values» (список структур). Мне нужно разделить имя. У меня есть список имен столбцов в качестве разделителя. Мне нужно проверить появление имен столбцов в списке, если присутствует одно из имен столбцов, а затем разделить фрейм данных.
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, explode
from pyspark.sql.types import StructType, StructField, StringType, IntegerType, ArrayType
value_schema = ArrayType(
StructType([
StructField("name", StringType(), True),
StructField("location", StringType(), True)
])
)
data = [
(1, [
{"name": "col1_US", "location": "usa"},
{"name": "col2_name_plex", "location": "usa"},
{"name": "col4_false", "location": "usa"},
{"name": "col3_name_is_fantasy", "location": "usa"}
])
]
df = spark.createDataFrame(data, ["id", "values"])
df = df.withColumn("values", explode(col("values")).alias("values"))
df = df.select(col("id"),col("values.name").alias("name"))
df.display()
col_names = ["col1","col2_name","col3_name_is","col4"]
for c in col_names:
#if (df["name"].contains(c)): # this is not working
split_data = split(df["name"], f'{c}_')
df = df.withColumns({
"new_name": lit(c),
"new_value": split_data.getItem(1)
})
df.display()
Данные после очистки:
id name
1 col1_US
1 col2_name_plex
1 col4_false_val
1 col3_name_is_fantasy
Окончательные данные из приведенного выше сценария:
# returning unexpected data
id name new_name new_value
1 col1_US col4 null
1 col2_name_plex col4 null
1 col4_false_val col4 false
1 col3_name_is_fantasy col4 null
Ожидаемый результат:
id name new_name new_value
1 col1_US col1 US
1 col2_name_plex col2_name plex
1 col4_false_val col4 false_val
1 col3_name_is_fantasy col3_name_is fantasy
Мы можем использовать регулярное выражение, чтобы создать поле new_name
из вашего списка желаемых col_names
:
col_names = ["col1","col2_name","col3_name_is","col4"]
pattern = "|".join(col_names)
df = df.withColumn("new_name", regexp_extract("name", pattern, 0))
+---+--------------------+------------+
| id| name| new_name|
+---+--------------------+------------+
| 1| col1_US| col1|
| 1| col2_name_plex| col2_name|
| 1| col4_false| col4|
| 1|col3_name_is_fantasy|col3_name_is|
+---+--------------------+------------+
Затем мы можем разделить, используя другое регулярное выражение, где мы передаем new_name
в качестве шаблона, который мы хотим сопоставить, и после этого получать только буквенно-цифровые символы (чтобы мы не добавляли «_»).
df.withColumn(
"new_value",
expr("regexp_replace(split(name, new_name)[1], '[^a-zA-Z0-9]+', '')")
)
+---+--------------------+------------+---------+
| id| name| new_name|new_value|
+---+--------------------+------------+---------+
| 1| col1_US| col1| US|
| 1| col2_name_plex| col2_name| plex|
| 1| col4_false| col4| false|
| 1|col3_name_is_fantasy|col3_name_is| fantasy|
+---+--------------------+------------+---------+
также попробовал код ниже, столкнулся с той же ошибкой df = df.withColumn( "new_value", split(df['name'], concat(df['new_name'], lit('_')))[1] )
опубликовано stackoverflow.com/questions/78671358/…
Спасибо. это работает в большинстве случаев. Я пропустил один случай, когда часть значения может иметь несколько пробелов. например, «col3_name_is_fantasy_val», здесь new_name — это «col3_name_is», а new_value — это «fantasy_val». Приведенный выше код обрабатывает new_name, но new_value имеет значение «fantasyval». Как справиться с такими случаями, я попробовал что-то вроде `split(df['name'], concat(df['new_name'],lit('_')))[1] ` но получаю ошибку. Столбец не повторяется .