วิธีอ่านไฟล์ข้อความหลายไฟล์ใน RDD เดียว?


179

ฉันต้องการอ่านไฟล์ข้อความจำนวนมากจากตำแหน่ง hdfs และทำการแมปในไฟล์ซ้ำโดยใช้ spark

JavaRDD<String> records = ctx.textFile(args[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 ที่นี่เป็นเพียงการเปิดเผยFileInputFormat APIของHadoopดังนั้นจึงมีการPathใช้ตัวเลือกเดียวกันทั้งหมด
Nick Chammas

7
sc.wholeTextFilesมีประโยชน์สำหรับข้อมูลที่ไม่ได้กำหนดเส้นแบ่ง
Michal Čizmazia

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 syntax Scala ที่เทียบเท่าจะเป็นsc.textFile(files.mkString(","))
Davos

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
  }

ตอนนี้ผ่านรายการวัตถุนี้ไปยังชิ้นส่วนของรหัสต่อไปนี้หมายเหตุ: sc เป็นวัตถุของ SQLContext

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

ตอนนี้คุณได้รับ Unified RDD ขั้นสุดท้ายเช่น df

เป็นทางเลือกและคุณยังสามารถแบ่งพาร์ติชันได้ใน BigRDD เดียว

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

การแบ่งใหม่ใช้งานได้เสมอ: D


นี่ไม่ได้หมายความว่ารายการไฟล์จะต้องมีขนาดค่อนข้างเล็กใช่ไหม ไม่ใช่ไฟล์นับล้าน
Mathieu Longtin

2
เราสามารถทำขนานการดำเนินการของการอ่านไฟล์ที่ระบุไว้ได้หรือไม่? สิ่งที่ต้องการ sc.parallelize?
lazywiz

1
@MathieuLongtin: หากคุณสามารถใช้การค้นพบพาร์ทิชันกับรหัส Spark ของคุณแล้วมันจะดีอย่างอื่นที่คุณต้องทำเช่นเดียวกัน ฉันเคยเปิดไฟล์ 10k ในเวลาประมาณหนึ่งนาที
Murtaza Kanchwala

@lazywiz หากคุณไม่ต้องการสร้าง rdd เพียงรายการเดียวให้ลบการดำเนินการแบ่งพาร์ติชันออก
Murtaza Kanchwala

3

ใน PySpark ฉันพบวิธีที่มีประโยชน์เพิ่มเติมในการแยกวิเคราะห์ไฟล์ บางทีอาจมี Scala ที่เทียบเท่า แต่ฉันไม่สะดวกพอที่จะได้คำแปลที่ใช้ได้ มันคือการโทร textFile ด้วยการเพิ่มป้ายกำกับ (ในตัวอย่างด้านล่าง key = filename, 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)

เอาต์พุต: อาร์เรย์ที่มีแต่ละรายการที่มี tuple โดยใช้ชื่อไฟล์ตามคีย์และมีค่า = แต่ละบรรทัดของไฟล์ (ในทางเทคนิคการใช้วิธีนี้คุณยังสามารถใช้คีย์อื่นนอกเหนือจากชื่อไฟล์พา ธ ที่แท้จริงซึ่งอาจเป็นตัวแทนการแปลงแป้นพิมพ์เพื่อบันทึกในหน่วยความจำ) กล่าวคือ

[('/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

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) 
    ...

ข้อ จำกัด อย่างหนึ่งคือเราต้องโหลดไฟล์ขนาดเล็กมิฉะนั้นประสิทธิภาพจะไม่ดีและอาจนำไปสู่ ​​OOM

บันทึก :

  • Wholefile ควรพอดีกับหน่วยความจำ
  • ดีสำหรับรูปแบบไฟล์ที่ไม่สามารถแบ่งย่อยได้ตามบรรทัด ... เช่นไฟล์ XML

อ้างอิงเพิ่มเติมเพื่อเยี่ยมชม


หรือเพียงแค่sc.wholeTextFiles(folder).flatMap...
Evhz

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

1

มีวิธีแก้ปัญหาตรงไปตรงมาสะอาดใช้ได้ ใช้เมธอด wholeTextFiles () การทำเช่นนี้จะทำให้ไดเรกทอรีและเป็นคู่ค่าคีย์ RDD ที่ส่งคืนจะเป็นคู่ RDD ค้นหาคำอธิบายจากSpark docsด้านล่าง:

SparkContext.wholeTextFiles ช่วยให้คุณอ่านไดเรกทอรีที่มีไฟล์ข้อความขนาดเล็กหลายไฟล์และส่งกลับแต่ละคู่เป็น (ชื่อไฟล์เนื้อหา) นี่ตรงกันข้ามกับ textFile ซึ่งจะส่งกลับหนึ่งระเบียนต่อบรรทัดในแต่ละไฟล์


-1

ลอง อินเทอร์เฟซนี้ใช้เพื่อเขียน DataFrame ไปยังระบบจัดเก็บข้อมูลภายนอก (เช่นระบบไฟล์ร้านค้าคีย์ - ค่า ฯลฯ ) ใช้ DataFrame.write () เพื่อเข้าถึงสิ่งนี้

ใหม่ในรุ่น 1.4

csv (เส้นทาง, โหมด = ไม่มี, การบีบอัด = None, sep = None, quote = None, escape = None, header = None, nullValue = None, escapeQuotes = None, quoteAll = None, dateFormat = None, timestampFormat = None บันทึก เนื้อหาของ DataFrame ในรูปแบบ CSV ที่เส้นทางที่ระบุ

พารามิเตอร์: path - พา ธ ในโหมดระบบไฟล์ที่รองรับ Hadoop - ระบุพฤติกรรมของการดำเนินการบันทึกเมื่อมีข้อมูลอยู่แล้ว

ผนวก: ผนวกเนื้อหาของ DataFrame นี้ไปยังข้อมูลที่มีอยู่ เขียนทับ: เขียนทับข้อมูลที่มีอยู่ ละเว้น: ละเว้นการดำเนินการนี้หากข้อมูลมีอยู่แล้ว ข้อผิดพลาด (กรณีเริ่มต้น): โยนข้อยกเว้นหากข้อมูลมีอยู่แล้ว การบีบอัด - ตัวแปลงสัญญาณการบีบอัดที่จะใช้เมื่อบันทึกเป็นไฟล์ นี่อาจเป็นหนึ่งในชื่อที่สั้นและไม่ตรงตามตัวพิมพ์ใหญ่ - เล็ก (ไม่มี, bzip2, gzip, lz4, เร็วและยุบ) sep - ตั้งค่าอักขระเดียวเป็นตัวคั่นสำหรับแต่ละฟิลด์และค่า หากไม่มีการตั้งค่ามันจะใช้ค่าเริ่มต้น,, quote - ตั้งค่าอักขระเดียวที่ใช้สำหรับการหลีกเลี่ยงค่าที่ยกมาโดยที่ตัวคั่นสามารถเป็นส่วนหนึ่งของค่า หากไม่มีการตั้งค่ามันจะใช้ค่าเริ่มต้น "ถ้าคุณต้องการปิดใบเสนอราคาคุณต้องตั้งค่าสตริงว่าง escape - ตั้งค่าอักขระเดี่ยวที่ใช้สำหรับหนีราคาภายในค่าที่ยกมาแล้วหากไม่มีการตั้งค่า , มันใช้ค่าเริ่มต้น \ escapeQuotes - ธงระบุว่าค่าที่มีคำพูดควรจะอยู่ในคำพูด หากไม่มีการตั้งค่ามันจะใช้ค่าเริ่มต้นจริงหนีค่าทั้งหมดที่มีตัวอักษรพูด quoteAll - แฟล็กที่ระบุว่าค่าทั้งหมดควรอยู่ในเครื่องหมายคำพูดเสมอหรือไม่ หากไม่มีการตั้งค่ามันจะใช้ค่าเริ่มต้นเป็นเท็จเพียงหลบหนีค่าที่มีตัวอักษรพูด header - เขียนชื่อของคอลัมน์เป็นบรรทัดแรก หากไม่มีการตั้งค่ามันจะใช้ค่าเริ่มต้นเป็นเท็จ nullValue - ตั้งค่าการแสดงสตริงของค่า null หากไม่มีการตั้งค่ามันจะใช้ค่าเริ่มต้นสตริงที่ว่างเปล่า dateFormat - ตั้งค่าสตริงที่ระบุรูปแบบวันที่ รูปแบบวันที่ที่กำหนดเองจะเป็นไปตามรูปแบบที่ java.text.SimpleDateFormat สิ่งนี้ใช้กับประเภทวันที่ หากไม่มีการตั้งค่ามันจะใช้ค่าเริ่มต้น yyyy-MM-dd timestampFormat - ตั้งค่าสตริงที่ระบุรูปแบบการประทับเวลา รูปแบบวันที่ที่กำหนดเองจะเป็นไปตามรูปแบบที่ java.text.SimpleDateFormat สิ่งนี้ใช้กับประเภทการประทับเวลา หากไม่มีการตั้งค่าจะใช้ค่าเริ่มต้น yyyy-MM-dd'T'HH: mm: ss.SSSZZ


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.