Làm cách nào để đọc nhiều tệp văn bản vào một RDD?


179

Tôi muốn đọc một loạt các tệp văn bản từ một vị trí hdfs và thực hiện ánh xạ trên nó trong một lần lặp bằng tia lửa.

JavaRDD<String> records = ctx.textFile(args[1], 1); có khả năng chỉ đọc một tệp tại một thời điểm.

Tôi muốn đọc nhiều hơn một tệp và xử lý chúng dưới dạng một RDD. Làm sao?

Câu trả lời:


298

Bạn có thể chỉ định toàn bộ thư mục, sử dụng ký tự đại diện và thậm chí CSV của thư mục và ký tự đại diện. Ví dụ:

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

Như Nick Chammas chỉ ra đây là một sự phơi bày của Hadoop FileInputFormatvà do đó, nó cũng hoạt động với Hadoop (và Scalding).


10
Đúng, đây là cách thuận tiện nhất để mở nhiều tệp dưới dạng một RDD. API ở đây chỉ là sự phơi bày của API FileInputFormat của Hadoop , vì vậy tất cả các Pathtùy chọn tương tự đều được áp dụng.
Nick Chammas

7
sc.wholeTextFilesthuận tiện cho dữ liệu không được phân định bằng dòng
Michal izmazia

1
Mặc dù điều này thật kỳ lạ nếu bạn làm điều này và chỉ định song song, nói rằng sc.textFile(multipleCommaSeparatedDirs,320)nó dẫn đến 19430tổng số nhiệm vụ thay vì 320... nó hoạt động như thế unioncũng dẫn đến số lượng nhiệm vụ điên rồ từ mức độ song song rất thấp
lisak

2
Cuối cùng tôi đã tìm thấy cách khớp mẫu tệp xấu này hoạt động stackoverflow.com/a/33917492/306488 vì vậy tôi không cần phải phân
tách

@femibyte Tôi không nghĩ vậy, mặc dù tôi không biết tại sao bạn muốn biết tên tệp trong bất kỳ tình huống nào khác ngoài wholeTextFiles. Trường hợp sử dụng của bạn là gì? Tôi có thể nghĩ về một cách giải quyết với điều kiện bạn sử dụng cùng số lượng phân vùng như các tệp ...
samthebest

35

Sử dụng unionnhư sau:

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

Sau đó, bigRddlà RDD với tất cả các tệp.


Cảm ơn bạn đám mây, bằng cách đó tôi có thể đọc tất cả các tệp tôi muốn, nhưng một! Nhưng tôi vẫn phải viết rất nhiều thứ ...
gsamaras

30

Bạn có thể sử dụng một lệnh gọi textFile để đọc nhiều tệp. Scala:

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

5
và cú pháp trăn giống hệt nhau
patricksurry

8
Tôi nghĩ rằng đó chỉ là cú pháp python. Tương đương Scala sẽ làsc.textFile(files.mkString(","))
Davos

9

Bạn có thể sử dụng cái này

Trước tiên, bạn có thể nhận được Bộ đệm / Danh sách các Đường dẫn 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
  }

Bây giờ Truyền đối tượng Danh sách này cho đoạn mã sau, lưu ý: sc là một đối tượng của SQLContext

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

Bây giờ bạn đã có một RDD hợp nhất cuối cùng, tức là df

Tùy chọn, và bạn cũng có thể phân vùng lại trong một BigRDD duy nhất

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

Phân vùng lại luôn hoạt động: D


Điều này không có nghĩa là danh sách tập tin phải tương đối nhỏ sao? Không phải hàng triệu tập tin.
Mathieu Longtin

2
Chúng ta có thể song song hoạt động đọc các tập tin được liệt kê? một cái gì đó như sc.metize?
lazywiz

1
@MathieuLongtin: Nếu bạn có thể áp dụng khám phá phân vùng cho mã Spark của mình thì sẽ rất tuyệt nếu bạn cần làm tương tự như vậy. Tôi đã sử dụng để mở các tệp 10k trong khoảng một phút.
Murtaza Kanchwala

@lazywiz Nếu bạn không muốn tạo một rdd duy nhất thì chỉ cần xóa hành động phân vùng lại.
Murtaza Kanchwala

3

Trong PySpark, tôi đã tìm thấy một cách hữu ích khác để phân tích các tệp. Có lẽ có một tương đương trong Scala, nhưng tôi không đủ thoải mái để đưa ra một bản dịch làm việc. Trên thực tế, đó là một cuộc gọi textFile với việc thêm nhãn (trong ví dụ dưới đây là key = filename, value = 1 dòng từ tệp).

TextFile "được gắn nhãn"

đầu vào:

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)

đầu ra: mảng với mỗi mục chứa một tuple bằng cách sử dụng tên tệp-as-key và với value = mỗi dòng tệp. (Về mặt kỹ thuật, sử dụng phương pháp này, bạn cũng có thể sử dụng một khóa khác bên cạnh tên filepath thực tế - có lẽ là biểu diễn băm để lưu vào bộ nhớ). I E.

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

Bạn cũng có thể kết hợp lại dưới dạng danh sách các dòng:

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

Hoặc kết hợp lại toàn bộ các tệp trở lại các chuỗi đơn (trong ví dụ này, kết quả giống như những gì bạn nhận được từ WholeTextFiles, nhưng với chuỗi "tệp:" bị tước khỏi filepathing.):

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


Khi tôi chạy dòng mã này - Spark_Full += sc.textFile(filename).keyBy(lambda x: filename) tôi đã nhận được lỗi tức là TypeError: 'PipelinedRDD' object is not iterable. Sự hiểu biết của tôi là, dòng đó tạo ra một RDD không thay đổi, vì vậy tôi đã tự hỏi làm thế nào bạn có thể nối nó với một biến khác?
KartikKannapur

3

bạn có thể dùng

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

Tại đây bạn sẽ nhận được đường dẫn của tệp và nội dung của tệp đó. để bạn có thể thực hiện bất kỳ hành động nào của toàn bộ tệp tại một thời điểm giúp tiết kiệm chi phí


2

Tất cả các câu trả lời đều đúng với sc.textFile

Tôi chỉ tự hỏi tại sao không wholeTextFilesVí dụ, trong trường hợp này ...

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

một hạn chế là, chúng tôi phải tải các tệp nhỏ nếu không hiệu suất sẽ kém và có thể dẫn đến OOM.

Ghi chú :

  • Các wholefile phải phù hợp với bộ nhớ
  • Tốt cho các định dạng tệp KHÔNG thể chia theo dòng ... chẳng hạn như các tệp XML

Tham khảo thêm để lần


hoặc chỉsc.wholeTextFiles(folder).flatMap...
Evhz

sc.wholeTextFiles (Thời gian / đường dẫn / đến / thư mục)
Ram Ghadiyaram

1

Có một giải pháp sạch thẳng về phía trước có sẵn. Sử dụng phương thức WholeTextFiles (). Điều này sẽ có một thư mục và tạo thành một cặp giá trị quan trọng. RDD được trả về sẽ là một cặp RDD. Tìm bên dưới mô tả từ tài liệu Spark :

SparkContext.wholeTextFiles cho phép bạn đọc một thư mục chứa nhiều tệp văn bản nhỏ và trả về mỗi tệp dưới dạng cặp (tên tệp, nội dung). Điều này trái ngược với textFile, sẽ trả về một bản ghi trên mỗi dòng trong mỗi tệp


-1

Giao diện TRY NÀY được sử dụng để ghi DataFrame cho các hệ thống lưu trữ bên ngoài (ví dụ: hệ thống tệp, lưu trữ khóa-giá trị, v.v.). Sử dụng DataFrame.write () để truy cập này.

Mới trong phiên bản 1.4.

csv (path, mode = none, nén = none, sep = none, quote = none, esc = none, header = none, nullValue = none, escQuotes = none, quoteAll = none, dateFormat = none, timestampFormat = none) nội dung của DataFrame ở định dạng CSV tại đường dẫn đã chỉ định.

Tham số: đường dẫn - đường dẫn trong bất kỳ chế độ hệ thống tệp được hỗ trợ của Hadoop - chỉ định hành vi của hoạt động lưu khi dữ liệu đã tồn tại.

chắp thêm: Nối nội dung của DataFrame này vào dữ liệu hiện có. ghi đè: Ghi đè dữ liệu hiện có. bỏ qua: Âm thầm bỏ qua thao tác này nếu dữ liệu đã tồn tại. lỗi (trường hợp mặc định): Ném ngoại lệ nếu dữ liệu đã tồn tại. nén - nén codec để sử dụng khi lưu vào tập tin. Đây có thể là một trong những tên rút ngắn không phân biệt chữ hoa chữ thường (none, bzip2, gzip, lz4, snappy và def def). sep - đặt ký tự đơn làm dấu phân cách cho từng trường và giá trị. Nếu Không được đặt, nó sử dụng giá trị mặc định ,. quote - đặt ký tự đơn được sử dụng để thoát các giá trị được trích dẫn trong đó dấu phân cách có thể là một phần của giá trị. Nếu Không được đặt, nó sử dụng giá trị mặc định ". Nếu bạn muốn tắt trích dẫn, bạn cần đặt một chuỗi trống. Thoát - đặt ký tự đơn được sử dụng để thoát dấu ngoặc kép trong một giá trị đã được trích dẫn. Nếu Không được đặt , nó sử dụng giá trị mặc định, \ escQuotes - Một cờ cho biết liệu các giá trị có chứa dấu ngoặc kép phải luôn được đặt trong dấu ngoặc kép hay không. Nếu Không được đặt, nó sử dụng giá trị mặc định là true, thoát tất cả các giá trị có chứa ký tự trích dẫn. quoteAll - Một cờ cho biết liệu tất cả các giá trị phải luôn được đặt trong dấu ngoặc kép. Nếu Không được đặt, nó sử dụng giá trị mặc định là false, chỉ thoát các giá trị có chứa ký tự trích dẫn. tiêu đề - ghi tên của các cột làm dòng đầu tiên. Nếu Không được đặt, nó sử dụng giá trị mặc định, sai. nullValue - đặt biểu diễn chuỗi của giá trị null. Nếu Không được đặt, nó sử dụng giá trị mặc định, chuỗi rỗng. dateFormat - đặt chuỗi cho biết định dạng ngày. Các định dạng ngày tùy chỉnh tuân theo các định dạng tại java.text.SimpleDateFormat. Điều này áp dụng cho loại ngày. Nếu Không được đặt, nó sử dụng giá trị giá trị mặc định, yyyy-MM-dd. timestampFormat - đặt chuỗi cho biết định dạng dấu thời gian. Các định dạng ngày tùy chỉnh tuân theo các định dạng tại java.text.SimpleDateFormat. Điều này áp dụng cho loại dấu thời gian. Nếu Không được đặt, nó sử dụng giá trị giá trị mặc định, yyyy-MM-dd'T'HH: mm: ss.SSSZZ.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.