Comment vérifier si Spark Dataframe est vide?

101

En ce moment, je dois utiliser df.count > 0pour vérifier si leDataFrame est vide ou non. Mais c'est un peu inefficace. Y a-t-il une meilleure façon de faire cela?

Merci.

PS: je veux vérifier s'il est vide pour ne sauvegarder que DataFrames'il n'est pas vide

auxdx
la source

Réponses:

154

Pour Spark 2.1.0, ma suggestion serait d'utiliser head(n: Int)ou take(n: Int)avec isEmpty, celui qui a l'intention la plus claire pour vous.

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

avec l'équivalent Python:

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

Utiliser df.first()et df.head()renverra tous les deux le java.util.NoSuchElementExceptionsi le DataFrame est vide. first()appelle head()directement, qui appelle head(1).head.

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

head(1)renvoie un Array, donc prendre headcet Array provoque le java.util.NoSuchElementExceptionlorsque le DataFrame est vide.

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

Donc, au lieu d'appeler head(), utilisez head(1)directement pour obtenir le tableau et vous pouvez ensuite utiliser isEmpty.

take(n)équivaut également à head(n)...

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

Et limit(1).collect()est équivalent à head(1)(remarquez limit(n).queryExecutiondans la head(n: Int)méthode), donc les éléments suivants sont tous équivalents, du moins d'après ce que je peux dire, et vous n'aurez pas à attraper une java.util.NoSuchElementExceptionexception lorsque le DataFrame est vide.

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

Je sais que c'est une question plus ancienne, donc j'espère qu'elle aidera quelqu'un à utiliser une version plus récente de Spark.

hulin003
la source
19
Pour ceux qui utilisent pyspark. isEmpty n'est pas une chose. Faites plutôt len ​​(d.head (1))> 0.
AntiPawn79 du
5
pourquoi est-ce mieux alors df.rdd.isEmpty?
Dan Ciborowski - MSFT
1
df.head (1) .isEmpty prend énormément de temps existe-t-il une autre solution optimisée pour cela.
Rakesh Sabbani
1
Salut @Rakesh Sabbani, si cela df.head(1)prend beaucoup de temps, c'est probablement parce que votre dfplan d'exécution fait quelque chose de compliqué qui empêche Spark de prendre des raccourcis. Par exemple, si vous lisez simplement des fichiers parquet df = spark.read.parquet(...), je suis presque sûr que Spark ne lira qu'une seule partition de fichiers. Mais si vous dffaites d'autres choses comme des agrégations, vous pouvez forcer par inadvertance Spark à lire et à traiter une grande partie, sinon la totalité, de vos données sources.
hulin003
juste rapporter mon expérience à ÉVITER: j'utilisais df.limit(1).count()naïvement. Sur les grands ensembles de données, cela prend beaucoup plus de temps que les exemples rapportés par @ hulin003 qui sont presque instantanés
Vzzarr
45

Je dirais simplement de saisir le sous-jacent RDD. Dans Scala:

df.rdd.isEmpty

en Python:

df.rdd.isEmpty()

Cela étant dit, tout cela ne fait qu'appeler take(1).length, donc ça fera la même chose que Rohan a répondu ... juste peut-être un peu plus explicite?

Justin Pihony
la source
6
C'est étonnamment plus lent que df.count () == 0 dans mon cas
architectonique
2
La conversion en rdd n'est-elle pas une tâche lourde?
Alok
1
Pas vraiment. Les RDD sont toujours le fondement de tout Spark pour la plupart.
Justin Pihony
28
Ne convertissez pas le df en RDD. Cela ralentit le processus. Si vous le convertissez, il convertira le DF entier en RDD et vérifiera s'il est vide. Pensez que si DF a des millions de lignes, la conversion en RDD lui-même prend beaucoup de temps.
Nandakishore
3
.rdd ralentit tellement le processus comme beaucoup
Raul H
14

Vous pouvez profiter des fonctions head()(ou first()) pour voir si le DataFramea une seule ligne. Si c'est le cas, il n'est pas vide.

Rohan Aletty
la source
10
si dataframe est vide, il lance "java.util.NoSuchElementException: next on empty iterator"; [Spark 1.3.1]
FelixHo
6

Si vous le faites df.count > 0. Il prend le décompte de toutes les partitions sur tous les exécuteurs et les additionne dans Driver. Cela prend un certain temps lorsque vous traitez des millions de lignes.

La meilleure façon de faire est d'effectuer df.take(1)et de vérifier si sa valeur null. Cela reviendra java.util.NoSuchElementExceptiontellement mieux de faire un essai df.take(1).

Le dataframe renvoie une erreur quand take(1)est terminé au lieu d'une ligne vide. J'ai mis en évidence les lignes de code spécifiques où cela génère l'erreur.

entrez la description de l'image ici

Nandakishore
la source
1
si vous l'exécutez sur une trame de données massive avec des millions d'enregistrements, cette countméthode prendra un certain temps.
TheM00s3
2
J'ai dit la même chose, je ne sais pas pourquoi vous avez refusé.
Nandakishore
votre droit vous avez dit la même chose, malheureusement, je ne vous ai pas voté contre.
TheM00s3
Ohhh d'accord. Je suis désolé TheMoos3, mais celui qui l'a fait, veuillez observer la réponse et comprendre le concept.
Nandakishore
l'utilisation de df.take (1) lorsque le df est vide entraîne la récupération d'un ROW vide qui ne peut pas être comparé à null
LetsPlayYahtzee
6

Depuis Spark 2.4.0, il existe Dataset.isEmpty.

Sa mise en œuvre est:

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

Notez que a DataFramen'est plus une classe dans Scala, c'est juste un alias de type (probablement changé avec Spark 2.0):

type DataFrame = Dataset[Row]
Béryllium
la source
1
isEmpty est plus lent que df.head (1) .isEmpty
Sandeep540
@ Sandeep540 Vraiment? Référence? Votre proposition instancie au moins une ligne. L'implémentation Spark transporte simplement un nombre. head () utilise aussi limit (), le groupBy () ne fait vraiment rien, il est nécessaire d'obtenir un RelationalGroupedDataset qui à son tour fournit count (). Cela ne devrait donc pas être beaucoup plus lent. C'est probablement plus rapide dans le cas d'un ensemble de données qui contient beaucoup de colonnes (éventuellement des données imbriquées dénormalisées). Anway, vous devez taper moins :-)
Béryllium
5

Pour les utilisateurs Java, vous pouvez l'utiliser sur un ensemble de données:

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

Cela vérifie tous les scénarios possibles (vide, nul).

Abdennacer Lachiheb
la source
3

Dans Scala, vous pouvez utiliser des implicits pour ajouter les méthodes isEmpty()et nonEmpty()à l'API DataFrame, ce qui rendra le code un peu plus agréable à lire.

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

Ici, d'autres méthodes peuvent également être ajoutées. Pour utiliser la conversion implicite, utilisez import DataFrameExtensions._dans le fichier que vous souhaitez utiliser la fonctionnalité étendue. Ensuite, les méthodes peuvent être utilisées directement comme suit:

val df: DataFrame = ...
if (df.isEmpty) {
  // Do something
}
Shaido - Réintégrer Monica
la source
2

J'avais la même question, et j'ai testé 3 solutions principales:

  1. df! = null df.count> 0
  2. df.head (1) .isEmpty () comme @ hulin003 le suggère
  3. df.rdd.isEmpty comme le suggère @Justin Pihony

et bien sûr les 3 fonctionnent, cependant en terme de performance, voici ce que j'ai trouvé, en exécutant ces méthodes sur le même DF dans ma machine, en terme de temps d'exécution:

  1. cela prend ~ 9366 ms
  2. cela prend ~ 5607 ms
  3. cela prend ~ 1921 ms

donc je pense que la meilleure solution est df.rdd.isEmpty comme le suggère @Justin Pihony

un nom
la source
1
l'option 3 prend moins de temps, pourquoi la seconde?
thinkman
Oups, votre droite, j'utilise le 3ème, je mets à jour la réponse
aName le
par curiosité ... avec quelle taille DataFrames a-t-il été testé?
aiguofer
1

J'ai trouvé cela sur certains cas:

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

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

c'est la même chose pour "length" ou remplacez take () par head ()

[Solution] pour le problème que nous pouvons utiliser.

>>>df.limit(2).count() > 1
False
Shekhar Koirala
la source
1

Si vous utilisez Pypsark, vous pouvez également faire:

len(df.head(1)) > 0
Adelholzener
la source
1

Sur PySpark, vous pouvez également utiliser bool(df.head(1))pour obtenir une Truede Falsevaleur

Il renvoie Falsesi le dataframe ne contient aucune ligne

Bose
la source
0
df1.take(1).length>0

La takeméthode renvoie le tableau de lignes, donc si la taille du tableau est égale à zéro, il n'y a aucun enregistrement dans df.

Gopi A
la source
-1

dataframe.limit(1).count > 0

Cela déclenche également un travail, mais comme nous sélectionnons un enregistrement unique, même dans le cas d'un milliard d'enregistrements à l'échelle, la consommation de temps pourrait être beaucoup plus faible.

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

Jordan Morris
la source
Ce sont toutes de mauvaises options prenant presque le même temps
Pushpendra Jaiswal
@PushpendraJaiswal oui, et dans un monde de mauvaises options, nous devrions choisir la meilleure mauvaise option
Jordan Morris le
-2

Vous pouvez le faire comme:

val df = sqlContext.emptyDataFrame
if( df.eq(sqlContext.emptyDataFrame) )
    println("empty df ")
else 
    println("normal df")
sYer Wang
la source
1
ne nécessitera-t-il pas que les schemadeux dataframes ( sqlContext.emptyDataFrame& df) soient identiques pour pouvoir revenir true?
y2k-shubham
1
Cela ne fonctionnera pas. eqest hérité de AnyRefet teste si l'argument (that) est une référence à l'objet récepteur (this).
Alper t. Turker