Python与Spark Scala处理DataFrame去空行后行数不一致问题排查
问题分析:Pandas与Spark Scala删除指定列空值后行数差异
核心差异根源
你的两段代码逻辑并非完全等价,再加上空值定义、数据本身的差异,最终导致行数结果天差地别:
- Pandas 多次
dropna(subset=[col], inplace=True):逐步过滤每一列的空值行,最终仅保留所有指定列均无空值的行(每一步都剔除对应列的空值行,结果叠加)。 - Spark Scala
na.drop(Seq(cols)):默认使用how="any"规则,会删除指定列中任意一列存在空值的行——看似和Pandas逻辑一致,但两者对"空值"的判定范围、数据预处理环节可能存在差异。
排查与验证步骤
1. 确认原始数据集完全一致
先排除数据源差异:
- 检查Pandas和Spark的读取逻辑:是否读取了同一文件/数据源?是否有预过滤、分区读取导致的数据缺失?
- 分别打印原始数据的行数,确认两者处理的是同一份数据。
2. 核对空值定义的差异
Spark和Pandas对"空值"的判定范围不同,这是最常见的原因:
- Pandas
dropna仅识别None、NaN为有效空值,空字符串""、空格字符串" "不会被过滤。 - Spark
na.drop默认仅识别null、NaN,但如果数据中存在大量空字符串,会被当成有效值保留,而Pandas中这些行不会被过滤——反过来,如果Spark中提前把空字符串转为null,结果会和Pandas对齐。
对齐空值处理的验证代码
Pandas侧(模拟Spark的空值判定):
import pandas as pd import numpy as np cols_to_filter = ["model", "engine_type", "aircraft_type", "status", "ArrDelay", "issue_date", "manufacturer", "type", "tailnum", "DepDelay", "TaxiOut"] # 将空字符串转为NaN,对齐Spark的空值处理逻辑 dfplaneairport[cols_to_filter] = dfplaneairport[cols_to_filter].replace("", np.nan) # 一次性过滤所有指定列的空值 df_filtered = dfplaneairport.dropna(subset=cols_to_filter) print(df_filtered.shape)Spark侧(模拟Pandas的空值判定):
import org.apache.spark.sql.functions._ val colsToFilter = Seq("engine_type", "aircraft_type", "status", "model", "issue_date", "manufacturer", "type","ArrDelay", "DepDelay", "TaxiOut", "tailnum") # 仅过滤指定列中为null/NaN的行,忽略空字符串 val dfFiltered = dfairports.filter( colsToFilter.map(c => col(c).isNotNull && col(c).isNaN === false).reduce(_ && _) ) dfFiltered.count()
3. 统一代码逻辑,排除操作差异
将两段代码的过滤逻辑完全对齐:
- Pandas不要多次调用
dropna,改为一次性过滤所有指定列(和Spark逻辑一致):cols_to_filter = ["model", "engine_type", ...] # 列出所有需要过滤的列 df_filtered = dfplaneairport.dropna(subset=cols_to_filter) print(df_filtered.shape) - Spark可以显式指定
how="any"(默认就是这个值,用于确认逻辑):dfairports = dfairports.na.drop(how="any", subset=Seq("engine_type", ...)) dfairports.count()
4. 检查列名匹配性
确认两段代码中指定的列名完全一致,包括大小写、拼写:
- 比如Pandas中的
ArrDelay和Spark中的arrdelay(大小写不同)会导致过滤的列完全不同,最终结果差异巨大。 - 分别打印两个DataFrame的列名列表,逐行对比。
内容的提问来源于stack exchange,提问作者Work All
相关产品推荐
相关产品推荐

