现在,我必须使用 df.count > 0
来检查 DataFrame
是否为空。但这有点低效。有没有更好的方法来做到这一点?
PS:我想检查它是否为空,以便仅在不为空时保存 DataFrame
对于 Spark 2.1.0,我的建议是将 head(n: Int)
或 take(n: Int)
与 isEmpty
一起使用,以您最清楚的意图为准。
df.head(1).isEmpty
df.take(1).isEmpty
与 Python 等效:
len(df.head(1)) == 0 # or bool(df.head(1))
len(df.take(1)) == 0 # or bool(df.take(1))
如果 DataFrame 为空,则使用 df.first()
和 df.head()
都将返回 java.util.NoSuchElementException
。 first()
直接调用 head()
,后者调用 head(1).head
。
def first(): T = head()
def head(): T = head(1).head
head(1)
返回一个数组,因此在该数组上使用 head
会在 DataFrame 为空时导致 java.util.NoSuchElementException
。
def head(n: Int): Array[T] = withAction("head", limit(n).queryExecution)(collectFromPlan)
因此,不要调用 head()
,而是直接使用 head(1)
来获取数组,然后您可以使用 isEmpty
。
take(n)
也等价于 head(n)
...
def take(n: Int): Array[T] = head(n)
并且 limit(1).collect()
等价于 head(1)
(注意 head(n: Int)
方法中的 limit(n).queryExecution
),所以以下都是等价的,至少据我所知,您不必捕获 java.util.NoSuchElementException
DataFrame 为空时的异常。
df.head(1).isEmpty
df.take(1).isEmpty
df.limit(1).collect().isEmpty
我知道这是一个较老的问题,所以希望它可以帮助使用新版本 Spark 的人。
我会说只是抓住底层的RDD
。在斯卡拉:
df.rdd.isEmpty
在 Python 中:
df.rdd.isEmpty()
话虽这么说,所有这一切都是调用 take(1).length
,所以它会做与 Rohan 回答相同的事情......只是可能稍微更明确一点?
我有同样的问题,我测试了 3 个主要解决方案:
(df != null) && (df.count > 0) df.head(1).isEmpty() as @hulin003 建议 df.rdd.isEmpty() as @Justin Pihony 建议
当然,这 3 个作品,但是就性能而言,这是我发现的,当在我的机器上的同一个 DF 上执行这些方法时,就执行时间而言:
大约需要 9366 毫秒 大约需要 5607 毫秒 大约需要 1921 毫秒
因此,我认为最好的解决方案是 df.rdd.isEmpty()
正如@Justin Pihony 建议的那样
从 Spark 2.4.0 开始有 Dataset.isEmpty
。
它的 implementation 是:
def isEmpty: Boolean =
withAction("isEmpty", limit(1).groupBy().count().queryExecution) { plan =>
plan.executeCollect().head.getLong(0) == 0
}
请注意,DataFrame
在 Scala 中不再是一个类,它只是一个 type alias(可能已在 Spark 2.0 中更改):
type DataFrame = Dataset[Row]
.option("mode", "DROPMALFORMED")
并且 df.isEmpty
返回 false
而 df.head(1).isEmpty
返回了 true
的正确结果,因为...所有行格式错误(上游有人更改了我的架构)。
您可以利用 head()
(或 first()
)函数来查看 DataFrame
是否有单行。如果是这样,它不是空的。
如果您这样做 df.count > 0
。它计算所有执行程序中所有分区的计数,并将它们添加到 Driver。当您处理数百万行时,这需要一段时间。
最好的方法是执行 df.take(1)
并检查它是否为空。这将返回 java.util.NoSuchElementException
,因此最好尝试一下 df.take(1)
。
当 take(1)
完成而不是空行时,数据框返回错误。我已经突出显示了它引发错误的特定代码行。
https://i.stack.imgur.com/tBnaN.png
count
方法将需要一些时间。
对于 Java 用户,您可以在数据集上使用它:
public boolean isDatasetEmpty(Dataset<Row> ds) {
boolean isEmpty;
try {
isEmpty = ((Row[]) ds.head(1)).length == 0;
} catch (Exception e) {
return true;
}
return isEmpty;
}
这会检查所有可能的情况(空、空)。
在 Scala 中,您可以使用隐式添加方法 isEmpty()
和 nonEmpty()
到 DataFrame API,这将使代码更易于阅读。
object DataFrameExtensions {
implicit def extendedDataFrame(dataFrame: DataFrame): ExtendedDataFrame =
new ExtendedDataFrame(dataFrame: DataFrame)
class ExtendedDataFrame(dataFrame: DataFrame) {
def isEmpty(): Boolean = dataFrame.head(1).isEmpty // Any implementation can be used
def nonEmpty(): Boolean = !isEmpty
}
}
在这里,也可以添加其他方法。要使用隐式转换,请在要使用扩展功能的文件中使用 import DataFrameExtensions._
。之后,可以直接使用这些方法:
val df: DataFrame = ...
if (df.isEmpty) {
// Do something
}
如果您使用的是 Pyspark,您还可以执行以下操作:
len(df.head(1)) > 0
在 PySpark 上,您还可以使用此 bool(df.head(1))
获取 True
的 False
值
如果数据框不包含任何行,则返回 False
PySpark 3.3.0+ / Scala 2.4.0+
df.isEmpty()
我发现在某些情况下:
>>>print(type(df))
<class 'pyspark.sql.dataframe.DataFrame'>
>>>df.take(1).isEmpty
'list' object has no attribute 'isEmpty'
这与“长度”相同或将 take() 替换为 head()
[解决方案] 对于我们可以使用的问题。
>>>df.limit(2).count() > 1
False
df1.take(1).length>0
take
方法返回行数组,因此如果数组大小等于 0,则 df
中没有记录。
假设我们有以下空数据框:
df = spark.sql("show tables").limit(0)
如果您使用的是 Spark 2.1,对于 pyspark,要检查此数据框是否为空,您可以使用:
df.count() > 0
或者
bool(df.head(1))
你可以这样做:
val df = sqlContext.emptyDataFrame
if( df.eq(sqlContext.emptyDataFrame) )
println("empty df ")
else
println("normal df")
sqlContext.emptyDataFrame
和 df
)的 schema
相同才能返回 true
?
eq
继承自 AnyRef
并测试参数 (that) 是否是对接收器对象 (this) 的引用。
dataframe.limit(1).count > 0
这也会触发作业,但由于我们选择的是单个记录,即使在十亿规模记录的情况下,时间消耗也可能要低得多。
不定期副业成功案例分享
df.rdd.isEmpty
好?df.head(1)
花费大量时间,那可能因为您的df
的执行计划正在执行一些复杂的操作,从而阻止 spark 走捷径。例如,如果您只是从 parquet 文件中读取df = spark.read.parquet(...)
,我很确定 spark 只会读取一个文件分区。但是,如果您的df
正在执行其他操作,例如聚合,您可能会无意中强制 spark 读取和处理大部分(如果不是全部)源数据。df.limit(1).count()
。在大型数据集上,它比@hulin003 报告的示例花费更多的时间,这些示例几乎是瞬时的