根据出现的第一个字符分隔字符串列

2024-04-25 20:35:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我想根据字符的第一次出现将Spark DataFrame的列分为两个不同的列,在本例中是下划线(“”)。在

我准备了一个100%可重复的例子:

mock Spark数据帧是:

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")
)

上面的代码创建如下表:

^{pr2}$

我想做的是根据第一个下划线从左到右的位置,将列city分为两个不同的列。在

最终需要的表格如下:

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_1link_2,等等),而是按字符串上所有特定字符的拆分;或者按字符串中字符的特定位置拆分。在

我也尝试过Python-Pandas方法,但正如预期的那样,它不适用于PySpark中的扩展或类比(link_3

提前谢谢你的帮助。在


Tags: citylink字符bostonlasparkwestsouth
2条回答

我认为这里最好的选择是使用pyspark.sql.functions.regexp_extract()和{}:

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连接收集的令牌。在

^{pr2}$

@pault已经用regexsplit和{}的内置函数给出了一个很好的答案

这里有一个简单的替代方法,使用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()

它应该给你

^{pr2}$

我希望答案是有帮助的

相关问题 更多 >