PySpark DataFrame词频统计时空白字符串的排查与过滤方法
PySpark词频统计空白异常值排查与解决
你写的清洗正则[^a-zA-Z]+?会删除所有非英文字母字符,所有纯符号、纯数字、带标点的词在清洗后会直接变成空字符串,这就是空值计数从423涨到3027的根本原因,和原始数据无关。
1、查看不可见字符串内容与Unicode编码的方法
默认的show()方法会把所有空白类字符渲染成普通空格,无法区分具体类型,用下面两种方式排查:
- 加边界标记看真实长度和内容
给待排查的字符串前后加固定标记,配合长度判断,就能看出是不是真的空串、有没有隐藏字符:
如果输出里from pyspark.sql.functions import length, concat, lit, col # 筛选长度小于3的疑似空白词,加边界标记输出 df2.filter(length(col("words")) < 3) \ .withColumn("debug_content", concat(lit("[START]"), col("words"), lit("[END]"))) \ .withColumn("str_length", length(col("words"))) \ .show(20, truncate=False)[START]和[END]中间看不到内容但长度大于0,就说明存在不可见字符。 - 打印字符Unicode编码定位具体字符
写UDF把字符串拆解成每个字符的Unicode编码,直接看到底是什么字符:
新闻标题里常见的不可见空白一般是:不间断空格from pyspark.sql.functions import udf from pyspark.sql.types import ArrayType, StringType get_codepoint = udf(lambda s: [f"U+{ord(c):04X}" for c in s], ArrayType(StringType())) df2.filter(length(col("words")) < 3) \ .withColumn("codepoints", get_codepoint(col("words"))) \ .select("words", "codepoints") \ .show(20, truncate=False)U+00A0、全角空格U+3000、零宽空格U+200B、制表符/换行符U+0009/U+000A,或者特殊符号、emoji拆分后残留的控制字符。
2、正确过滤异常空白字符串的方案
你最初出现空值的核心原因是拆分逻辑有问题:直接用split(' ')按单个空格拆分时,如果标题存在连续空格、首尾空格,就会拆出空字符串,比如"hello world"(两个空格)拆分后会得到["hello", "", "world"]。按下面步骤处理可以彻底解决空值问题:
- 第一步:拆分前标准化标题空白
先把所有类型的空白字符统一替换成普通空格,去掉首尾空格,再按任意空白正则拆分,从源头避免拆分出空串:from pyspark.sql.functions import split, explode, trim, regexp_replace, lower, length import nltk nltk.download('stopwords') from nltk.corpus import stopwords # 标准化所有空白字符,trim首尾空格 df_process = df.withColumn("title", trim(regexp_replace(col("title"), r"\s+", " "))) # 按任意空白拆分,自动跳过连续空白,不会产生空串 df_words = df_process.withColumn("words", explode(split(col("title"), r"\s+"))) \ .select("words") - 第二步:优化单词清洗逻辑
不要直接删除所有非字母字符,只去掉单词首尾的标点,保留单词中间的合法字符(比如U.S.、don't里的点和撇号),避免正常词汇被洗成空串:# 移除单词首尾的非字母字符 df_words = df_words.withColumn("words", regexp_replace(col("words"), r"^[^a-zA-Z]+|[^a-zA-Z]+$", "")) # 过滤长度为0的空串 df_words = df_words.filter(length(col("words")) > 0) - 第三步:过滤停用词+残留特殊字符
统一转小写匹配停用词,避免手动维护大小写版本的停用词表,再额外过滤Unicode分类下的所有空白、控制字符残留:stoplist = set(stopwords.words('english') + ['|','—','-','–']) # 移除所有Unicode空白、控制类字符 df_words = df_words.withColumn("words", regexp_replace(col("words"), r"[\p{Z}\p{C}]", "")) \ .filter(length(col("words")) > 0) # 过滤停用词后统计词频 word_count = df_words.filter(~lower(col("words")).isin(stoplist)) \ .groupBy("words") \ .count() \ .orderBy("count", ascending=False) word_count.show()
内容的提问来源于stack exchange,提问作者Mohamed Yasser
相关产品推荐
相关产品推荐

