複数のテキストファイルを単一のRDDに読み込む方法は?


179

hdfsの場所から一連のテキストファイルを読み取り、sparkを使用して繰り返しマッピングします。

JavaRDD<String> records = ctx.textFile(args[1], 1); は、一度に1つのファイルのみを読み取ることができます。

複数のファイルを読み取り、それらを単一のRDDとして処理したい。どうやって?

回答:


299

ディレクトリ全体を指定したり、ワイルドカードを使用したり、ディレクトリとワイルドカードのCSVを使用したりすることもできます。例えば:

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

Nick Chammasが指摘するように、これはHadoopの露出でFileInputFormatあり、したがってこれはHadoop(およびScalding)でも機能します。


10
はい、これは複数のファイルを単一のRDDとして開く最も便利な方法です。ここでのAPIは、HadoopのFileInputFormat APIの公開であり、同じPathオプションがすべて適用されます。
Nick Chammas 14年

7
sc.wholeTextFiles行で区切られていないデータに便利です
MichalČizmazia15年

1
ただし、これを実行して並列処理を指定すると、sc.textFile(multipleCommaSeparatedDirs,320)それが19430タスク全体ではなく合計タスクにつながると言うのは奇妙ですが、非常に低い並列処理から非常に多くのタスクが発生320するように動作unionします
lisak

2
この邪悪なファイルパターンマッチングがどのように機能するかをついに見つけましたstackoverflow.com/a/33917492/306488なので、コンマで区切る必要はありません
lisak

@femibyte私はそうは思いませんが、以外の状況でファイル名を知りたい理由はわかりませんwholeTextFiles。あなたのユースケースは何ですか?ファイルと同じ数のパーティションを使用するという条件で回避策を考えることができます...
samthebest

35

union次のように使用します。

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

次に、bigRddすべてのファイルを含むRDDです。


クラウドありがとうございます。そうすれば、必要なすべてのファイルを読み取ることができます。しかし、それでも、私はたくさんのことを書かなければなりません...
gsamaras

30

単一のtextFile呼び出しを使用して、複数のファイルを読み取ることができます。Scala:

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

5
および同一のpython構文
patricksurry

8
私はそれがpython構文だけだと思います。Scalaでの同等機能は次のようになりますsc.textFile(files.mkString(","))
ダボス2017年

9

これを使えます

まず、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
  }

次に、このListオブジェクトを次のコードに渡します。注意:scはSQLContextのオブジェクトです

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

これで、最終的な統合RDD、つまりdfが手に入りました

オプションで、単一のBigRDDに再パーティション化することもできます

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

再パーティション化は常に機能します:D


これは、ファイルリストを比較的小さくする必要があることを意味しませんか?数百万のファイルではありません。
Mathieu Longtin、2016

2
リストされたファイルを読み取る操作を並列化できますか?sc.parallelizeのようなもの?
lazywiz 2016年

1
@MathieuLongtin:パーティション検出をSparkコードに適用できる場合は、それと同じようにそれを行う必要があります。以前は約1分間で10kファイルを開いていました。
Murtaza Kanchwala 16年

@lazywiz単一のrddを作成したくない場合は、再パーティションアクションを削除してください。
Murtaza Kanchwala 16年

3

PySparkで、ファイルを解析するための追加の便利な方法を見つけました。おそらくScalaに同等のものがありますが、私は実用的な翻訳を思いつくのに十分満足できません。実際には、ラベルが追加されたtextFile呼び出しです(以下の例では、key =ファイル名、value =ファイルから1行です)。

「ラベル付き」textFile

入力:

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)

出力:filename-as-keyを使用するタプルを含み、value =ファイルの各行を含む各エントリを持つ配列。(技術的には、この方法を使用すると、実際のファイルパス名の他に別のキーを使用することもできます-おそらくハッシュ表現でメモリを節約できます)。すなわち。

[('/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'),
  ...]

行のリストとして再結合することもできます。

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'])]

または、ファイル全体を単一の文字列に再結合します(この例では、結果はwholeTextFilesから取得したものと同じですが、文字列 "file:"がファイルパスから削除されます)。

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


このコード行を実行するとSpark_Full += sc.textFile(filename).keyBy(lambda x: filename) 、エラーが発生しましたTypeError: 'PipelinedRDD' object is not iterable。私の理解では、その行は不変のRDDを作成するので、それを別の変数にどのように追加できるのかと思っていましたか?
KartikKannapur 2017年

3

あなたは使うことができます

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

ここでは、ファイルのパスとそのファイルのコンテンツを取得します。したがって、一度にファイル全体の任意のアクションを実行して、オーバーヘッドを節約できます


2

すべての答えは正しいです sc.textFile

なぜだろうと思っていたwholeTextFilesたとえば、この場合...

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

1つの制限は、小さなファイルをロードする必要があることです。そうしないと、パフォーマンスが低下し、OOMにつながる可能性があります。

注意 :

  • ファイル全体がメモリに収まる必要があります
  • XMLファイルなど、行ごとに分割できないファイル形式に適しています。

さらに参照訪問


または単にsc.wholeTextFiles(folder).flatMap...
Evhz 2018

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

1

利用可能な簡単なクリーンソリューションがあります。wholeTextFiles()メソッドを使用します。これはディレクトリを取り、キーと値のペアを形成します。返されるRDDはペアのRDDになります。以下のSpark docsの説明を見つけてください。

SparkContext.wholeTextFilesを使用すると、複数の小さなテキストファイルを含むディレクトリを読み取り、それぞれを(ファイル名、コンテンツ)のペアとして返すことができます。これは、各ファイルの行ごとに1つのレコードを返すtextFileとは対照的です


-1

これを試して インタフェースは、外部ストレージ・システム(たとえば、ファイルシステム、キーと値の店舗など)にデータフレームを書き込むために使用されます。これにアクセスするには、DataFrame.write()を使用します。

バージョン1.4の新機能。

csv(path、mode = None、compression = None、sep = None、quote = None、escape = None、header = None、nullValue = None、escapeQuotes = None、quoteAll = None、dateFormat = None、timestampFormat = None)を保存します指定されたパスにあるCSV形式のDataFrameのコンテンツ。

パラメータ:path – Hadoopでサポートされているファイルシステムモードのパス–データがすでに存在する場合の保存操作の動作を指定します。

append:このDataFrameのコンテンツを既存のデータに追加します。overwrite:既存のデータを上書きします。ignore:データが既に存在する場合、この操作をサイレントに無視します。エラー(デフォルトの場合):データがすでに存在する場合、例外をスローします。圧縮–ファイルに保存するときに使用する圧縮コーデック。これは、大文字と小文字を区別しない既知の短縮名(none、bzip2、gzip、lz4、snappy、deflate)のいずれかです。sep –各フィールドと値の区切り文字として単一の文字を設定します。Noneが設定されている場合、デフォルト値、を使用します。quote –引用符で囲まれた値をエスケープするために使用される単一の文字を設定します。セパレーターは値の一部になります。Noneが設定されている場合は、デフォルト値の "が使用されます。引用符をオフにしたい場合は、空の文字列を設定する必要があります。escape –引用符で囲まれた値内の引用符のエスケープに使用される単一文字を設定します。Noneが設定されている場合、デフォルト値\ escapeQuotes –引用符を含む値を常に引用符で囲む必要があるかどうかを示すフラグ。Noneが設定されている場合は、デフォルト値trueを使用して、引用文字を含むすべての値をエスケープします。quoteAll –すべての値を常に引用符で囲む必要があるかどうかを示すフラグ。Noneが設定されている場合は、デフォルト値falseを使用し、引用文字を含む値のみをエスケープします。header –列の名前を最初の行として書き込みます。Noneが設定されている場合、デフォルト値のfalseが使用されます。nullValue-null値の文字列表現を設定します。Noneが設定されている場合は、デフォルト値の空の文字列が使用されます。dateFormat –日付形式を示す文字列を設定します。カスタム日付形式は、java.text.SimpleDateFormatの形式に従います。これは日付タイプに適用されます。Noneが設定されている場合、デフォルト値yyyy-MM-ddが使用されます。timestampFormat –タイムスタンプ形式を示す文字列を設定します。カスタム日付形式は、java.text.SimpleDateFormatの形式に従います。これはタイムスタンプタイプに適用されます。Noneが設定されている場合、デフォルト値yyyy-MM-dd'T'HH:mm:ss.SSSZZが使用されます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.