我想根据字符的首次出现将Spark DataFrame的列分为2个不同的列,在本例中为下划线(“ _”)。
我准备了一个100%可复制的示例:
模拟的Spark DataFrame是:
df = spark.createDataFrame(
[
(1, 1.8, 'newyork_3434_north'),
(4, 2.6, 'la_432432432_south'),
(6, 3.3, 'boston_234324_east'),
(8, 4.1, 'detroit_6757_west'),
(2, 5.7, 'miami_133123_north'),
(3, 6.2, 'atlanta_093394_west'),
(1, 6.1, 'houston_87342_east')
],
('ranking', "coordenate", "city")
)
以上代码创建了一个类似于以下内容的表:
ranking coordenate city
1 1.8 newyork_3434_north
4 2.6 la_432432432_south
6 3.3 boston_234324_east
8 4.1 detroit_6757_west
2 5.7 miami_133123_north
3 6.2 atlanta_093394_west
1 6.1 houston_87342_east
我想根据左下划线的位置将列城市分为2个不同的列。
最终所需的表将是这样的:
ranking coordenate city code
1 1.8 newyork 3434_north
4 2.6 la 432432432_south
6 3.3 boston 234324_east
8 4.1 detroit 6757_west
2 5.7 miami 133123_north
3 6.2 atlanta 093394_west
1 6.1 houston 87342_east
我已经看到有关此主题的多个主题,但是他们没有谈论字符的首次出现(link_1,link_2等),而是按出现的所有特定字符进行划分一串或按字符在字符串中的特定位置拆分。
我也尝试过Python Pandas方法,但按预期,它不能通过扩展或类比(link_3)应用于PySpark
预先感谢您的帮助。
答案 0 :(得分:3)
我认为最好的选择是使用pyspark.sql.functions.regexp_extract()
和pyspark.sql.functions.regexp_replace()
:
import pyspark.sql.functions as f
df.select(
"ranking",
"coordenate",
f.regexp_extract("city", pattern="^[A-Za-z]+(?=_)", idx=0).alias('city'),
f.regexp_replace("city", "^[A-Za-z]+_", "").alias("code")
).show()
#+-------+----------+----------+---------------+
#|ranking|coordenate| city| code|
#+-------+----------+----------+---------------+
#| 1| 1.8| newyork| 3434_north|
#| 4| 2.6| la|432432432_south|
#| 6| 3.3| boston| 234324_east|
#| 8| 4.1| detroit| 6757_west|
#| 2| 5.7| miami| 133123_north|
#| 3| 6.2| atlanta| 093394_west|
#| 1| 6.1| houston| 87342_east|
#+-------+----------+----------+---------------+
在两种情况下,模式本质上都是相同的:
^[A-Za-z]+
:匹配从字符串开头开始的任意数量的字母(?=_)
:对下划线的肯定前瞻对于city
,我们找到此模式并提取第一个匹配项。对于code
,我们将前瞻性更改为匹配项,然后将模式替换为空字符串。
如果很难找到合适的正则表达式模式,则这是一种适用于Spark 2.1及更高版本的替代方法:
获取city
很简单-您可以使用pyspark.sql.functions.split()
在下划线处拆分字符串,然后使用getItem(0)
获取拆分列表的第一个元素。
对于code
部分,在下划线处拆分city
,然后使用pyspark.sql.functions.posexplode()
展开结果数组。然后筛选出pos > 0
,按原始列分组,然后使用pyspark.sql.functions.concat_ws
加入收集的令牌。
df.select(
"*",
f.posexplode(f.split("city", "_")).alias("pos", "token")
)\
.where("pos > 0")\
.groupBy("ranking", "coordenate", "city")\
.agg(f.concat_ws("_" ,f.collect_list("token")).alias("code"))\
.select(
"ranking",
"coordenate",
f.split("city", "_").getItem(0).alias("city"),
"code"
)\
.show()
答案 1 :(得分:0)
@pault已使用regex
和split
和concat_ws
内置函数给出了一个可怕的答案
使用udf
函数作为
from pyspark.sql.functions import col, udf
from pyspark.sql.types import ArrayType, StringType
@udf(ArrayType(StringType()))
def splitUdf(x):
splitted = x.split('_')
return [splitted[0], '_'.join(splitted[1:])]
df.withColumn('city', splitUdf(col('city')))\
.select(col('ranking'), col('coordenate'), col('city')[0].alias('city'), col('city')[1].alias('code'))\
.show()
应该给您
+-------+----------+-------+---------------+
|ranking|coordenate| city| code|
+-------+----------+-------+---------------+
| 1| 1.8|newyork| 3434_north|
| 4| 2.6| la|432432432_south|
| 6| 3.3| boston| 234324_east|
| 8| 4.1|detroit| 6757_west|
| 2| 5.7| miami| 133123_north|
| 3| 6.2|atlanta| 093394_west|
| 1| 6.1|houston| 87342_east|
+-------+----------+-------+---------------+
我希望答案会有所帮助