Come verificare se il dataframe spark è vuoto?


102

In questo momento, devo usare df.count > 0per verificare se DataFrameè vuoto o meno. Ma è un po 'inefficiente. C'è un modo migliore per farlo?

Grazie.

PS: voglio controllare se è vuoto in modo da salvare solo DataFramese non è vuoto

Risposte:


155

Per Spark 2.1.0, il mio suggerimento sarebbe quello di utilizzare head(n: Int)o take(n: Int)con isEmpty, a seconda di quale ha l'intento più chiaro per te.

df.head(1).isEmpty
df.take(1).isEmpty

con l'equivalente Python:

len(df.head(1)) == 0  # or bool(df.head(1))
len(df.take(1)) == 0  # or bool(df.take(1))

L'uso di df.first()e df.head()restituirà entrambi java.util.NoSuchElementExceptionse il DataFrame è vuoto. first()chiama head()direttamente, che chiama head(1).head.

def first(): T = head()
def head(): T = head(1).head

head(1)restituisce un Array, quindi assumere quell'Array headcausa java.util.NoSuchElementExceptionquando il DataFrame è vuoto.

def head(n: Int): Array[T] = withAction("head", limit(n).queryExecution)(collectFromPlan)

Quindi, invece di chiamare head(), usa head(1)direttamente per ottenere l'array e poi puoi usare isEmpty.

take(n)è anche equivalente a head(n)...

def take(n: Int): Array[T] = head(n)

Ed limit(1).collect()è equivalente a head(1)(nota limit(n).queryExecutionnel head(n: Int)metodo), quindi i seguenti sono tutti equivalenti, almeno da quello che posso dire, e non dovrai catturare java.util.NoSuchElementExceptionun'eccezione quando il DataFrame è vuoto.

df.head(1).isEmpty
df.take(1).isEmpty
df.limit(1).collect().isEmpty

So che questa è una domanda precedente, quindi spero che aiuti qualcuno che utilizza una versione più recente di Spark.


20
Per coloro che utilizzano pyspark. isEmpty non è una cosa. Invece len (d.head (1))> 0.
AntiPawn79

5
perché è meglio allora df.rdd.isEmpty?
Dan Ciborowski - MSFT

1
df.head (1) .isEmpty sta impiegando molto tempo se esiste un'altra soluzione ottimizzata per questo.
Rakesh Sabbani

1
Ehi @ Rakesh Sabbani, se ci df.head(1)vuole molto tempo, probabilmente è perché il tuo dfpiano di esecuzione sta facendo qualcosa di complicato che impedisce a Spark di prendere scorciatoie. Ad esempio, se stai leggendo solo da file parquet df = spark.read.parquet(...), sono abbastanza sicuro che Spark leggerà solo una partizione di file. Ma se dfstai facendo altre cose come le aggregazioni, potresti inavvertitamente forzare Spark a leggere ed elaborare una gran parte, se non tutti, dei tuoi dati di origine.
hulin003

riferendo solo la mia esperienza a EVITARE: stavo usando df.limit(1).count()ingenuamente. Su grandi set di dati ci vuole molto più tempo rispetto agli esempi riportati da @ hulin003 che sono quasi istantanei
Vzzarr

45

Direi di afferrare solo il sottostante RDD. In Scala:

df.rdd.isEmpty

in Python:

df.rdd.isEmpty()

Detto questo, tutto ciò che fa è chiamare take(1).length, quindi farà la stessa cosa che ha risposto Rohan ... solo forse un po 'più esplicito?


6
Questo è sorprendentemente più lento di df.count () == 0 nel mio caso
Architectonic

2
La conversione in rdd non è un compito pesante?
Alok

1
Non proprio. Gli RDD sono ancora alla base di tutto Spark per la maggior parte.
Justin Pihony

28
Non convertire il df in RDD. Rallenta il processo. Se lo converti, convertirà l'intero DF in RDD e controllerà se è vuoto. Pensa se DF ha milioni di righe, richiede molto tempo per la conversione in RDD stesso.
Nandakishore

3
.rdd rallenta così tanto il processo come molto
Raul H

14

Puoi sfruttare le funzioni head()(o first()) per vedere se DataFrameha una singola riga. Se è così, non è vuoto.


10
se dataframe è vuoto, lancia "java.util.NoSuchElementException: next on empty iterator"; [Spark 1.3.1]
FelixHo

6

Se lo fai df.count > 0. Prende il conteggio di tutte le partizioni su tutti gli esecutori e le somma in Driver. Questo richiede un po 'di tempo quando hai a che fare con milioni di righe.

Il modo migliore per farlo è eseguire df.take(1)e controllare se è nullo. Questo tornerà java.util.NoSuchElementExceptionquindi meglio provare df.take(1).

Il dataframe restituisce un errore quando take(1)viene eseguito invece di una riga vuota. Ho evidenziato le righe di codice specifiche in cui genera l'errore.

inserisci qui la descrizione dell'immagine


1
se lo esegui su un enorme dataframe con milioni di record, quel countmetodo richiederà del tempo.
TheM00s3

2
Ho detto la stessa cosa, non sono sicuro del motivo per cui hai dato un pollice in giù.
Nandakishore

hai ragione hai detto la stessa cosa, sfortunatamente, non ti ho screditato.
TheM00s3

Ohhh okay. Mi dispiace TheMoos3, ma chiunque l'abbia fatto per favore osserva la risposta e capisci il concetto.
Nandakishore

utilizzando df.take (1) quando il df è vuoto si ottiene una RIGA vuota che non può essere paragonata a null
LetsPlayYahtzee

6

A partire da Spark 2.4.0 esiste Dataset.isEmpty.

La sua implementazione è:

def isEmpty: Boolean = 
  withAction("isEmpty", limit(1).groupBy().count().queryExecution) { plan =>
    plan.executeCollect().head.getLong(0) == 0
}

Nota che a DataFramenon è più una classe in Scala, è solo un alias di tipo (probabilmente modificato con Spark 2.0):

type DataFrame = Dataset[Row]

1
isEmpty è più lento di df.head (1) .isEmpty
Sandeep540

@ Sandeep540 Davvero? Prova delle prestazioni? La tua proposta istanzia almeno una riga. L'implementazione di Spark trasporta solo un numero. head () usa anche limit (), groupBy () non sta facendo nulla, è necessario per ottenere un RelationalGroupedDataset che a sua volta fornisce count (). Quindi non dovrebbe essere significativamente più lento. Probabilmente è più veloce nel caso di un set di dati che contiene molte colonne (possibilmente dati annidati denormalizzati). Comunque devi digitare meno :-)
Beryllium

5

Per gli utenti Java puoi usarlo su un set di dati:

public boolean isDatasetEmpty(Dataset<Row> ds) {
        boolean isEmpty;
        try {
            isEmpty = ((Row[]) ds.head(1)).length == 0;
        } catch (Exception e) {
            return true;
        }
        return isEmpty;
}

Questo controlla tutti i possibili scenari (vuoto, nullo).


3

In Scala puoi usare gli impliciti per aggiungere i metodi isEmpty()e nonEmpty()l'API DataFrame, che renderà il codice un po 'più piacevole da leggere.

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
  }
}

Qui possono essere aggiunti anche altri metodi. Per utilizzare la conversione implicita, utilizza import DataFrameExtensions._nel file che desideri utilizzare la funzionalità estesa. Successivamente, i metodi possono essere utilizzati direttamente in questo modo:

val df: DataFrame = ...
if (df.isEmpty) {
  // Do something
}

2

Ho avuto la stessa domanda e ho testato 3 soluzioni principali:

  1. df! = null df.count> 0
  2. df.head (1) .isEmpty () come suggerisce @ hulin003
  3. df.rdd.isEmpty come suggerisce @Justin Pihony

e ovviamente i 3 funzionano, comunque in termini di perfermance, ecco cosa ho trovato, eseguendo questi metodi sullo stesso DF della mia macchina, in termini di tempo di esecuzione:

  1. ci vogliono ~ 9366 ms
  2. ci vogliono ~ 5607 ms
  3. ci vogliono ~ 1921 ms

quindi penso che la soluzione migliore sia df.rdd.isEmpty come suggerisce @Justin Pihony


1
l'opzione 3 richiede meno tempo, perché la seconda?
thinkman

Ops, hai ragione, sto usando il 3 °, aggiorno la risposta
aName

per curiosità ... con quali dimensioni DataFrames è stato testato?
aiguofer

1

Ho scoperto che in alcuni casi:

>>>print(type(df))
<class 'pyspark.sql.dataframe.DataFrame'>

>>>df.take(1).isEmpty
'list' object has no attribute 'isEmpty'

questo è lo stesso per "length" o sostituisci take () con head ()

[Soluzione] per il problema che possiamo utilizzare.

>>>df.limit(2).count() > 1
False


1

Su PySpark, è anche possibile utilizzare questo bool(df.head(1))per ottenere un Truedi Falsevalore

Restituisce Falsese il dataframe non contiene righe


0
df1.take(1).length>0

Il takemetodo restituisce l'array di righe, quindi se la dimensione dell'array è uguale a zero, non ci sono record in df.


-1

dataframe.limit(1).count > 0

Anche questo innesca un lavoro, ma poiché stiamo selezionando un singolo record, anche in caso di miliardi di record di scala il consumo di tempo potrebbe essere molto inferiore.

Da: https://medium.com/checking-emptiness-in-distributed-objects/count-vs-isempty-surprised-to-see-the-impact-fa70c0246ee0


Tutte queste sono cattive opzioni che
richiedono

@PushpendraJaiswal sì, e in un mondo di cattive opzioni, dovremmo scegliere la migliore cattiva opzione
Jordan Morris

-2

Puoi farlo come:

val df = sqlContext.emptyDataFrame
if( df.eq(sqlContext.emptyDataFrame) )
    println("empty df ")
else 
    println("normal df")

1
non richiederà che i schemadue dataframe ( sqlContext.emptyDataFrame& df) siano uguali per tornare mai true?
y2k-shubham

1
Questo non funzionerà. eqviene ereditato da AnyRefe verifica se l'argomento (that) è un riferimento all'oggetto ricevente (this).
Alper t. Turker
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.