Come leggere più file di testo in un singolo RDD?


179

Voglio leggere un mucchio di file di testo da una posizione hdfs ed eseguire il mapping su di esso in un'iterazione usando spark.

JavaRDD<String> records = ctx.textFile(args[1], 1); è in grado di leggere solo un file alla volta.

Voglio leggere più di un file ed elaborarli come un singolo RDD. Come?

Risposte:


299

È possibile specificare intere directory, utilizzare caratteri jolly e persino CSV di directory e caratteri jolly. Per esempio:

sc.textFile("/my/dir1,/my/paths/part-00[0-5]*,/another/dir,/a/specific/file")

Come sottolinea Nick Chammas, questa è un'esposizione di Hadoop FileInputFormate quindi funziona anche con Hadoop (e Scalding).


10
Sì, questo è il modo più conveniente per aprire più file come un singolo RDD. L'API qui è solo un'esposizione dell'API FileInputFormat di Hadoop , quindi si Pathapplicano tutte le stesse opzioni.
Nick Chammas,

7
sc.wholeTextFilesè utile per i dati che non sono delimitati da righe
Michal Čizmazia,

1
È strano però che se lo fai e specifichi il parallelismo, supponi sc.textFile(multipleCommaSeparatedDirs,320)che porti a 19430compiti totali invece di 320... si comporta come quello unionche porta anche a un numero folle di compiti da parallelismo molto basso
lisak

2
Alla

@femibyte Non la penso così, anche se non so perché vorresti conoscere il nome del file in una situazione diversa da quella di wholeTextFiles. Qual è il tuo caso d'uso? Mi viene in mente una soluzione alternativa a condizione che tu usi lo stesso numero di partizioni dei file ...
samthebest

35

Utilizzare unioncome segue:

val sc = new SparkContext(...)
val r1 = sc.textFile("xxx1")
val r2 = sc.textFile("xxx2")
...
val rdds = Seq(r1, r2, ...)
val bigRdd = sc.union(rdds)

Poi la bigRddè la RDD con tutti i file.


Grazie cloud, in questo modo posso leggere tutti i file che voglio, ma uno! Tuttavia, devo scrivere un sacco di cose ...
gsamaras,

30

È possibile utilizzare una singola chiamata textFile per leggere più file. Scala:

sc.textFile(','.join(files)) 

5
e identica sintassi del pitone
patricksurry

8
Penso che sia solo la sintassi di Python. L'equivalente alla Scala sarebbesc.textFile(files.mkString(","))
Davos, il

9

Puoi usare questo

Per prima cosa puoi ottenere un buffer / elenco di percorsi S3:

import scala.collection.JavaConverters._
import java.util.ArrayList
import com.amazonaws.services.s3.AmazonS3Client
import com.amazonaws.services.s3.model.ObjectListing
import com.amazonaws.services.s3.model.S3ObjectSummary
import com.amazonaws.services.s3.model.ListObjectsRequest

def listFiles(s3_bucket:String, base_prefix : String) = {
    var files = new ArrayList[String]

    //S3 Client and List Object Request
    var s3Client = new AmazonS3Client();
    var objectListing: ObjectListing = null;
    var listObjectsRequest = new ListObjectsRequest();

    //Your S3 Bucket
    listObjectsRequest.setBucketName(s3_bucket)

    //Your Folder path or Prefix
    listObjectsRequest.setPrefix(base_prefix)

    //Adding s3:// to the paths and adding to a list
    do {
      objectListing = s3Client.listObjects(listObjectsRequest);
      for (objectSummary <- objectListing.getObjectSummaries().asScala) {
        files.add("s3://" + s3_bucket + "/" + objectSummary.getKey());
      }
      listObjectsRequest.setMarker(objectListing.getNextMarker());
    } while (objectListing.isTruncated());

    //Removing Base Directory Name
    files.remove(0)

    //Creating a Scala List for same
    files.asScala
  }

Ora passa questo oggetto List al seguente pezzo di codice, nota: sc è un oggetto di SQLContext

var df: DataFrame = null;
  for (file <- files) {
    val fileDf= sc.textFile(file)
    if (df!= null) {
      df= df.unionAll(fileDf)
    } else {
      df= fileDf
    }
  }

Ora hai un RDD unificato finale, ovvero df

Opzionale, E puoi anche ripartizionarlo in un singolo BigRDD

val files = sc.textFile(filename, 1).repartition(1)

Il ripartizionamento funziona sempre: D


Questo non significa che l'elenco dei file deve essere relativamente piccolo? Non milioni di file.
Mathieu Longtin,

2
Possiamo parallelizzare l'operazione di lettura dei file elencati? qualcosa come sc.parallelize?
lazywiz,

1
@MathieuLongtin: se riesci ad applicare il rilevamento delle partizioni al tuo codice Spark, sarà fantastico altrimenti dovrai fare lo stesso. Ho aperto file da 10k in circa un minuto.
Murtaza Kanchwala

@lazywiz Se non si desidera creare un singolo rdd, rimuovere semplicemente l'azione di ripartizione.
Murtaza Kanchwala

3

In PySpark, ho trovato un modo utile aggiuntivo per analizzare i file. Forse c'è un equivalente in Scala, ma non mi sento abbastanza a mio agio nel trovare una traduzione funzionante. È, in effetti, una chiamata textFile con l'aggiunta di etichette (nell'esempio seguente la chiave = nomefile, valore = 1 riga dal file).

File di testo "con etichetta"

ingresso:

import glob
from pyspark import SparkContext
SparkContext.stop(sc)
sc = SparkContext("local","example") # if running locally
sqlContext = SQLContext(sc)

for filename in glob.glob(Data_File + "/*"):
    Spark_Full += sc.textFile(filename).keyBy(lambda x: filename)

output: array con ogni voce contenente una tupla usando il nome file come chiave e con valore = ogni riga del file. (Tecnicamente, usando questo metodo puoi anche usare una chiave diversa oltre al nome del percorso del file, forse una rappresentazione di hashing per salvare in memoria). vale a dire.

[('/home/folder_with_text_files/file1.txt', 'file1_contents_line1'),
 ('/home/folder_with_text_files/file1.txt', 'file1_contents_line2'),
 ('/home/folder_with_text_files/file1.txt', 'file1_contents_line3'),
 ('/home/folder_with_text_files/file2.txt', 'file2_contents_line1'),
  ...]

Puoi anche ricombinare come un elenco di righe:

Spark_Full.groupByKey().map(lambda x: (x[0], list(x[1]))).collect()

[('/home/folder_with_text_files/file1.txt', ['file1_contents_line1', 'file1_contents_line2','file1_contents_line3']),
 ('/home/folder_with_text_files/file2.txt', ['file2_contents_line1'])]

O ricombina interi file in singole stringhe (in questo esempio il risultato è lo stesso di quello che ottieni da interiTextFile, ma con la stringa "file:" rimossa dal percorso dei file.):

Spark_Full.groupByKey().map(lambda x: (x[0], ' '.join(list(x[1])))).collect()


Quando ho eseguito questa riga di codice, Spark_Full += sc.textFile(filename).keyBy(lambda x: filename) ho ricevuto l'errore cioè TypeError: 'PipelinedRDD' object is not iterable. La mia comprensione è che quella linea crea un RDD che è immutabile, quindi mi chiedevo come hai potuto aggiungerlo a un'altra variabile?
Kartik Kannapur,

3

Puoi usare

JavaRDD<String , String> records = sc.wholeTextFiles("path of your directory")

qui otterrai il percorso del tuo file e il contenuto di quel file. in modo da poter eseguire qualsiasi azione di un intero file in un momento che salva l'overhead


2

Tutte le risposte sono corrette con sc.textFile

Mi stavo solo chiedendo perché no wholeTextFilesAd esempio, in questo caso ...

val minPartitions = 2
val path = "/pathtohdfs"
    sc.wholeTextFiles(path,minPartitions)
      .flatMap{case (path, text) 
    ...

una limitazione è che, dobbiamo caricare piccoli file altrimenti le prestazioni saranno pessime e potrebbero portare a OOM.

Nota :

  • L'intero file dovrebbe adattarsi alla memoria
  • Buono per formati di file che non sono divisibili per linea ... come i file XML

Ulteriore riferimento da visitare


o solosc.wholeTextFiles(folder).flatMap...
Evhz il

sc.wholeTextFiles (“/ path / to / dir”)
Ram Ghadiyaram,

1

È disponibile una soluzione pulita semplice. Utilizzare il metodo wholeTextFiles (). Questo richiederà una directory e forma una coppia valore-chiave. Il RDD restituito sarà un RDD di coppia. Di seguito la descrizione dai documenti Spark :

SparkContext.wholeTextFiles consente di leggere una directory contenente più piccoli file di testo e restituisce ciascuno di essi come coppie (nome file, contenuto). Ciò è in contrasto con textFile, che restituirebbe un record per riga in ciascun file


-1

PROVA QUESTO interfaccia utilizzata per scrivere un DataFrame su sistemi di archiviazione esterni (ad es. File system, archivi di valori-chiave, ecc.). Utilizzare DataFrame.write () per accedere a questo.

Novità nella versione 1.4.

csv (path, mode = None, compressione = None, sep = None, quote = None, escape = None, header = None, nullValue = None, escapeQuotes = None, quoteAll = None, dateFormat = None, timestampFormat = None) Salva il contenuto del DataFrame in formato CSV nel percorso specificato.

Parametri: path - il percorso in qualsiasi modalità file system supportata da Hadoop - specifica il comportamento dell'operazione di salvataggio quando i dati esistono già.

append: aggiunge il contenuto di questo DataFrame ai dati esistenti. sovrascrivi: sovrascrive i dati esistenti. ignora: ignora silenziosamente questa operazione se i dati esistono già. errore (caso predefinito): genera un'eccezione se i dati esistono già. compressione - codec di compressione da utilizzare per il salvataggio su file. Questo può essere uno dei nomi abbreviati maiuscoli / minuscoli noti (nessuno, bzip2, gzip, lz4, snappy e deflate). sep - imposta il singolo carattere come separatore per ciascun campo e valore. Se è impostato Nessuno, utilizza il valore predefinito,,. quote: imposta il singolo carattere utilizzato per sfuggire ai valori tra virgolette in cui il separatore può far parte del valore. Se è impostato Nessuno, utilizza il valore predefinito ". Se si desidera disattivare le virgolette, è necessario impostare una stringa vuota. Escape - imposta il singolo carattere utilizzato per sfuggire alle virgolette all'interno di un valore già quotato. Se Nessuno è impostato , utilizza il valore predefinito, \ escapeQuotes - Un flag che indica se i valori contenenti virgolette devono essere sempre racchiusi tra virgolette. Se è impostato Nessuno, utilizza il valore predefinito true, che sfugge a tutti i valori contenenti un carattere di virgoletta. quoteAll - Un flag che indica se tutti i valori devono essere sempre racchiusi tra virgolette. Se impostato su Nessuno, utilizza il valore predefinito false, che sfugge solo ai valori contenenti un carattere virgolette. header - scrive i nomi delle colonne come prima riga. Se è impostato Nessuno, utilizza il valore predefinito, falso. nullValue: imposta la rappresentazione di stringa di un valore null. Se è impostato Nessuno, utilizza il valore predefinito, stringa vuota. dateFormat: imposta la stringa che indica un formato data. I formati di data personalizzati seguono i formati su java.text.SimpleDateFormat. Questo vale per il tipo di data. Se è impostato Nessuno, utilizza il valore predefinito, yyyy-MM-dd. timestampFormat: imposta la stringa che indica un formato timestamp. I formati di data personalizzati seguono i formati su java.text.SimpleDateFormat. Questo vale per il tipo di data e ora. Se è impostato Nessuno, utilizza il valore predefinito, yyyy-MM-dd'T'HH: mm: ss.SSSZZ.


-4
rdd = textFile('/data/{1.txt,2.txt}')
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.