Como verificar se o dataframe do Spark está vazio?

101

Agora, tenho que usar df.count > 0para verificar se o DataFrameestá vazio ou não. Mas é meio ineficiente. Existe alguma maneira melhor de fazer isso?

Obrigado.

PS: Quero verificar se está vazio para só salvar o DataFramese não estiver vazio

auxdx
fonte

Respostas:

154

Para o Spark 2.1.0, minha sugestão seria usar head(n: Int)ou take(n: Int)com isEmpty, o que tiver a intenção mais clara para você.

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

com Python equivalente:

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

Usar df.first()e df.head()retornará o java.util.NoSuchElementExceptionse o DataFrame estiver vazio. first()liga head()diretamente, que liga head(1).head.

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

head(1)retorna um Array, portanto, assumir headesse Array causa o java.util.NoSuchElementExceptionquando o DataFrame está vazio.

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

Portanto, em vez de chamar head(), use head(1)diretamente para obter o array e, em seguida, você pode usar isEmpty.

take(n)também é equivalente a head(n)...

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

E limit(1).collect()é equivalente a head(1)(observe limit(n).queryExecutionno head(n: Int)método), então java.util.NoSuchElementExceptionos itens a seguir são todos equivalentes, pelo menos pelo que eu posso dizer, e você não terá que pegar uma exceção quando o DataFrame estiver vazio.

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

Eu sei que esta é uma pergunta mais antiga, então espero que ajude alguém que está usando uma versão mais recente do Spark.

hulin003
fonte
19
Para aqueles que usam o pyspark. isEmpty não é uma coisa. Faça len (d.head (1))> 0 em vez disso.
AntiPawn79 de
4
por que isso é melhor então df.rdd.isEmpty?
Dan Ciborowski - MSFT de
1
df.head (1) .isEmpty está demorando muito se houver alguma outra solução otimizada para isso.
Rakesh Sabbani de
1
Olá @Rakesh Sabbani, Se df.head(1)está demorando muito, provavelmente é porque seu dfplano de execução está fazendo algo complicado que impede o Spark de pegar atalhos. Por exemplo, se você está apenas lendo arquivos do parquet df = spark.read.parquet(...), tenho certeza que o spark lerá apenas uma partição do arquivo. Mas se você dfestá fazendo outras coisas como agregações, pode estar forçando inadvertidamente o Spark a ler e processar uma grande parte, se não todos, de seus dados de origem.
hulin003
apenas relatando minha experiência para EVITAR: Eu estava usando df.limit(1).count()ingenuamente. Em grandes conjuntos de dados, leva muito mais tempo do que os exemplos relatados por @ hulin003 que são quase instantâneos
Vzzarr
45

Eu diria apenas para pegar o subjacente RDD. Em Scala:

df.rdd.isEmpty

em Python:

df.rdd.isEmpty()

Dito isso, tudo que isso faz é ligar take(1).length, então fará a mesma coisa que Rohan respondeu ... apenas talvez um pouco mais explícito?

Justin Pihony
fonte
6
Isso é surpreendentemente mais lento que df.count () == 0 no meu caso
arquitetônico
2
Converter para o rdd não é uma tarefa difícil?
Alok
1
Na verdade não. Os RDDs ainda são a base de tudo o que é Spark na maior parte.
Justin Pihony
28
Não converta o df em RDD. Isso retarda o processo. Se você converter, ele converterá DF inteiro em RDD e verificará se está vazio. Pense se o DF tem milhões de linhas, leva muito tempo para converter para o próprio RDD.
Nandakishore 01 de
3
.rdd retarda tanto o processo como muito
Raul H
14

Você pode aproveitar as funções head()(ou first()) para ver se o DataFrametem uma única linha. Nesse caso, não está vazio.

Rohan Aletty
fonte
10
se o dataframe estiver vazio, ele lança "java.util.NoSuchElementException: next on empty iterator"; [Spark 1.3.1]
FelixHo
6

Se você fizer df.count > 0 . Ele pega a contagem de todas as partições em todos os executores e as adiciona no Driver. Isso demora um pouco quando você está lidando com milhões de linhas.

A melhor maneira de fazer isso é executar df.take(1)e verificar se é nulo. Isso vai voltar, java.util.NoSuchElementExceptionentão é melhor tentar df.take(1).

O dataframe retorna um erro quando take(1)é concluído, em vez de uma linha vazia. Eu destaquei as linhas de código específicas onde ele lança o erro.

insira a descrição da imagem aqui

Nandakishore
fonte
1
se você executar isso em um dataframe massivo com milhões de registros, esse countmétodo levará algum tempo.
TheM00s3
2
Eu disse o mesmo, não sei por que você deu um polegar para baixo.
Nandakishore
você está certo, você disse o mesmo, infelizmente, eu não votei contra você.
TheM00s3
Ohhh ok. Sinto muito TheMoos3, mas quem fez isso observe a resposta e entenda o conceito.
Nandakishore
usar df.take (1) quando o df está vazio resulta na obtenção de uma ROW vazia que não pode ser comparada com null
LetsPlayYahtzee
6

Desde Spark 2.4.0 existe Dataset.isEmpty.

Sua implementação é:

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

Observe que a DataFramenão é mais uma classe em Scala, é apenas um alias de tipo (provavelmente alterado com Spark 2.0):

type DataFrame = Dataset[Row]
Berílio
fonte
1
isEmpty é mais lento que df.head (1) .isEmpty
Sandeep540
@ Sandeep540 Sério? Referência? Sua proposta instancia pelo menos uma linha. A implementação do Spark apenas transporta um número. head () está usando limit () também, o groupBy () não está realmente fazendo nada, é necessário obter um RelationalGroupedDataset que, por sua vez, fornece count (). Portanto, isso não deve ser significativamente mais lento. Provavelmente é mais rápido no caso de um conjunto de dados que contém muitas colunas (possivelmente dados aninhados desnormalizados). De qualquer forma, você deve digitar menos :-)
Berílio
5

Para usuários de Java, você pode usar isso em um conjunto de dados:

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

Isso verifica todos os cenários possíveis (vazio, nulo).

Abdennacer Lachiheb
fonte
3

No Scala, você pode usar implícitos para adicionar os métodos isEmpty()e nonEmpty()à API DataFrame, o que tornará o código um pouco mais agradável de ler.

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

Aqui, outros métodos também podem ser adicionados. Para usar a conversão implícita, use import DataFrameExtensions._no arquivo que deseja usar a funcionalidade estendida. Posteriormente, os métodos podem ser usados ​​diretamente da seguinte forma:

val df: DataFrame = ...
if (df.isEmpty) {
  // Do something
}
Shaido - Reintegrar Monica
fonte
2

Eu tive a mesma pergunta e testei 3 soluções principais:

  1. df! = nulo df.count> 0
  2. df.head (1) .isEmpty () como @ hulin003 sugere
  3. df.rdd.isEmpty como @Justin Pihony sugere

e claro os 3 trabalhos, porém em termos de desempenho, aqui está o que eu encontrei, ao executar esses métodos no mesmo DF da minha máquina, em termos de tempo de execução:

  1. leva ~ 9366ms
  2. leva ~ 5607ms
  3. leva ~ 1921ms

portanto, acho que a melhor solução é df.rdd.isEmpty como @Justin Pihony sugere

um nome
fonte
1
a opção 3 leva menos tempo, por que a segunda?
thinkman
Opa, você está certo, estou usando o terceiro, atualizo a resposta
aName
por curiosidade ... com que tamanho de DataFrames isso foi testado?
aiguofer
1

Descobri isso em alguns casos:

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

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

isso é o mesmo para "comprimento" ou substitua take () por head ()

[Solução] para o problema que podemos usar.

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

Se estiver usando o Pypsark, você também pode fazer:

len(df.head(1)) > 0
Adelholzener
fonte
1

No PySpark, você também pode usar isso bool(df.head(1))para obter um TruedeFalse valor

Ele retorna Falsese o dataframe não contém linhas

Bose
fonte
0
df1.take(1).length>0

O takemétodo retorna a matriz de linhas, portanto, se o tamanho da matriz for igual a zero, não haverá registros em df.

Gopi A
fonte
-1

dataframe.limit(1).count > 0

Isso também aciona um trabalho, mas como estamos selecionando um único registro, mesmo no caso de bilhões de registros de escala, o consumo de tempo pode ser muito menor.

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

Jordan Morris
fonte
Todas essas opções são ruins, levando quase o mesmo tempo
Pushpendra Jaiswal
@PushpendraJaiswal sim, e em um mundo de opções ruins, devemos escolher a melhor opção ruim
Jordan Morris
-2

Você pode fazer assim:

val df = sqlContext.emptyDataFrame
if( df.eq(sqlContext.emptyDataFrame) )
    println("empty df ")
else 
    println("normal df")
sYer Wang
fonte
1
não vai exigir que o schemade dois dataframes ( sqlContext.emptyDataFrame& df) seja o mesmo para retornar true?
y2k-shubham
1
Isso não vai funcionar. eqé herdado de AnyRefe testa se o argumento (that) é uma referência ao objeto receptor (this).
Alper t. Turker