ฉันต้องการอ่านไฟล์ข้อความจำนวนมากจากตำแหน่ง hdfs และทำการแมปในไฟล์ซ้ำโดยใช้ spark
JavaRDD<String> records = ctx.textFile(args[1], 1);
สามารถอ่านไฟล์ได้ครั้งละหนึ่งไฟล์เท่านั้น
ฉันต้องการอ่านมากกว่าหนึ่งไฟล์และประมวลผลเป็น RDD เดียว อย่างไร?
ฉันต้องการอ่านไฟล์ข้อความจำนวนมากจากตำแหน่ง hdfs และทำการแมปในไฟล์ซ้ำโดยใช้ spark
JavaRDD<String> records = ctx.textFile(args[1], 1);
สามารถอ่านไฟล์ได้ครั้งละหนึ่งไฟล์เท่านั้น
ฉันต้องการอ่านมากกว่าหนึ่งไฟล์และประมวลผลเป็น RDD เดียว อย่างไร?
คำตอบ:
คุณสามารถระบุไดเรกทอรีทั้งหมดใช้สัญลักษณ์แทนและแม้แต่ CSV ของไดเรกทอรีและสัญลักษณ์แทน เช่น:
sc.textFile("/my/dir1,/my/paths/part-00[0-5]*,/another/dir,/a/specific/file")
ในฐานะที่เป็น Nick Chammas ชี้ให้เห็นว่านี่คือการสัมผัสของ Hadoop FileInputFormat
และดังนั้นจึงใช้งานได้กับ Hadoop (และ Scalding)
sc.wholeTextFiles
มีประโยชน์สำหรับข้อมูลที่ไม่ได้กำหนดเส้นแบ่ง
sc.textFile(multipleCommaSeparatedDirs,320)
มันจะนำไปสู่19430
งานทั้งหมดแทนที่จะเป็น320
... มันจะทำตัวเหมือนunion
ซึ่งนำไปสู่จำนวนงานที่บ้าจากการขนานที่ต่ำมาก
wholeTextFiles
กว่าสำหรับการใด กรณีการใช้งานของคุณคืออะไร? ฉันสามารถคิดวิธีแก้ปัญหาที่มีให้คุณใช้หมายเลขเดียวกันของพาร์ทิชันเป็นไฟล์ ...
ใช้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 พร้อมไฟล์ทั้งหมด
คุณสามารถใช้การโทร textFile เดียวเพื่ออ่านหลายไฟล์ Scala:
sc.textFile(','.join(files))
sc.textFile(files.mkString(","))
คุณสามารถใช้สิ่งนี้
ก่อนอื่นคุณจะได้รับบัฟเฟอร์ / รายการเส้นทาง 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
ใน 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 ที่ไม่เปลี่ยนรูปได้ดังนั้นฉันจึงสงสัยว่าคุณสามารถผนวกเข้ากับตัวแปรอื่นได้อย่างไร?
คุณสามารถใช้ได้
JavaRDD<String , String> records = sc.wholeTextFiles("path of your directory")
ที่นี่คุณจะได้รับเส้นทางของไฟล์และเนื้อหาของไฟล์นั้น เพื่อให้คุณสามารถดำเนินการกับไฟล์ทั้งหมดในเวลาที่บันทึกค่าใช้จ่าย
คำตอบทั้งหมดถูกต้องด้วย sc.textFile
ฉันแค่สงสัยว่าทำไมไม่wholeTextFiles
ยกตัวอย่างเช่นในกรณีนี้ ...
val minPartitions = 2
val path = "/pathtohdfs"
sc.wholeTextFiles(path,minPartitions)
.flatMap{case (path, text)
...
ข้อ จำกัด อย่างหนึ่งคือเราต้องโหลดไฟล์ขนาดเล็กมิฉะนั้นประสิทธิภาพจะไม่ดีและอาจนำไปสู่ OOM
บันทึก :
อ้างอิงเพิ่มเติมเพื่อเยี่ยมชม
sc.wholeTextFiles(folder).flatMap...
มีวิธีแก้ปัญหาตรงไปตรงมาสะอาดใช้ได้ ใช้เมธอด wholeTextFiles () การทำเช่นนี้จะทำให้ไดเรกทอรีและเป็นคู่ค่าคีย์ RDD ที่ส่งคืนจะเป็นคู่ RDD ค้นหาคำอธิบายจากSpark docsด้านล่าง:
SparkContext.wholeTextFiles ช่วยให้คุณอ่านไดเรกทอรีที่มีไฟล์ข้อความขนาดเล็กหลายไฟล์และส่งกลับแต่ละคู่เป็น (ชื่อไฟล์เนื้อหา) นี่ตรงกันข้ามกับ textFile ซึ่งจะส่งกลับหนึ่งระเบียนต่อบรรทัดในแต่ละไฟล์
ลอง อินเทอร์เฟซนี้ใช้เพื่อเขียน 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
rdd = textFile('/data/{1.txt,2.txt}')
Path
ใช้ตัวเลือกเดียวกันทั้งหมด