여러 개의 텍스트 파일을 단일 RDD로 읽는 방법은 무엇입니까?


179

hdfs 위치에서 많은 텍스트 파일을 읽고 spark를 사용하여 반복적으로 매핑하고 싶습니다.

JavaRDD<String> records = ctx.textFile(args[1], 1); 한 번에 하나의 파일 만 읽을 수 있습니다.

둘 이상의 파일을 읽고 단일 RDD로 처리하고 싶습니다. 어떻게?

답변:


298

전체 디렉토리를 지정하고 와일드 카드를 사용하고 디렉토리 및 와일드 카드의 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 2016 년

7
sc.wholeTextFiles라인 구분되지 않은 데이터에 편리합니다
Michal Čizmazia

1
이 작업을 수행하고 병렬 처리를 지정하면 대신 전체 작업으로 sc.textFile(multipleCommaSeparatedDirs,320)이어진다 고 말하는 것이 이상합니다 . 이는 매우 낮은 병렬 처리로 19430320union
인해 엄청난

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 호출을 사용하여 여러 파일을 읽을 수 있습니다. 스칼라 :

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

5
똑같은 파이썬 문법
patricksurry

8
나는 그것이 파이썬 구문 일뿐 이라고 생각 합니다. 스칼라에 해당하는 것은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
  }

이제이 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
    }
  }

이제 최종 Unified RDD 즉 df를 얻었습니다.

선택 사항이며 단일 BigRDD로 다시 파티션을 나눌 수도 있습니다

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

재 파티셔닝은 항상 작동합니다 : D


이것은 파일 목록이 상대적으로 작아야한다는 것을 의미하지 않습니까? 수백만 개의 파일이 아닙니다.
Mathieu Longtin

2
나열된 파일을 읽는 작업을 병렬화 할 수 있습니까? sc.parallelize 같은 것?
lazywiz

1
@MathieuLongtin : Spark 코드에 파티션 검색을 적용 할 수 있다면 다른 방법으로 수행해야합니다. 나는 약 1 분 안에 10k 파일을 열었습니다.
Murtaza Kanchwala

@lazywiz 만약 당신이 하나의 rdd를 만들고 싶지 않다면 repartition action을 제거하십시오.
Murtaza Kanchwala

3

PySpark에서 파일을 구문 분석하는 추가 유용한 방법을 찾았습니다. 아마도 스칼라에는 동등한 것이 있지만, 나는 일하는 번역을 내기에 충분히 편안하지 않습니다. 실제로 레이블이 추가 된 textFile 호출입니다 (아래 예에서는 키 = 파일 이름, 값 = 파일에서 한 줄).

"레이블이있는"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를 사용하여 튜플을 포함하는 각 항목과 값 = 파일의 각 줄을 가진 배열. 기술적 으로이 방법을 사용하면 실제 파일 경로 이름 외에도 다른 키를 사용하여 메모리에 저장하는 해시 표현을 사용할 수 있습니다. 즉.

[('/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이 발생할 수 있습니다.

노트 :

  • 전체 파일은 메모리에 맞아야합니다
  • XML 파일과 같이 줄 단위로 분할 할 수없는 파일 형식에 적합

방문에 대한 추가 참조


또는 그냥sc.wholeTextFiles(folder).flatMap...
Evhz

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

1

바로 사용할 수있는 깨끗한 솔루션이 있습니다. wholeTextFiles () 메소드를 사용하십시오. 이것은 디렉토리를 가져와 키 값 쌍을 형성합니다. 리턴 된 RDD는 쌍 RDD입니다. Spark 문서 에서 설명을 아래에서 찾으십시오 .

SparkContext.wholeTextFiles를 사용하면 여러 개의 작은 텍스트 파일이 포함 된 디렉토리를 읽고 각 파일을 (파일 이름, 내용) 쌍으로 반환합니다. 이것은 각 파일에서 한 줄에 하나의 레코드를 반환하는 textFile과 대조적입니다.


-1

이 시도 스토리지 시도 외부 스토리지 시스템 (예 : 파일 시스템, 키-값 저장소 등)에 DataFrame을 쓰는 데 사용되는 인터페이스입니다. 이에 액세스하려면 DataFrame.write ()를 사용하십시오.

버전 1.4의 새로운 기능.

csv (경로, 모드 = 없음, 압축 = 없음, sep = 없음, 따옴표 = 없음, 이스케이프 = 없음, 헤더 = 없음, nullValue = 없음, escapeQuotes = 없음, quoteAll = None, dateFormat = None, timestampFormat = None) 지정된 경로에서 CSV 형식의 DataFrame 내용.

매개 변수 : path – 모든 Hadoop 지원 파일 시스템 모드의 경로 – 데이터가 이미 존재하는 경우 저장 작업의 동작을 지정합니다.

추가 :이 DataFrame의 내용을 기존 데이터에 추가합니다. 덮어 쓰기 : 기존 데이터를 덮어 씁니다. 무시 : 데이터가 이미 존재하는 경우이 작업을 자동으로 무시하십시오. error (default case) : 데이터가 이미 존재하면 예외를 던집니다. compression – 파일로 저장할 때 사용할 압축 코덱. 이것은 대소 문자를 구분하지 않는 알려진 단축 이름 중 하나 일 수 있습니다 (없음, bzip2, gzip, lz4, snappy 및 deflate). sep – 단일 문자를 각 필드와 값의 구분 기호로 설정합니다. 없음이 설정되면 기본값 인,가 사용됩니다. quote – 구분자가 값의 일부일 수있는 인용 된 값을 이스케이프하는 데 사용되는 단일 문자를 설정합니다. None을 설정하면 기본값 인 ""를 사용합니다. 따옴표를 끄려면 빈 문자열을 설정해야합니다. escape – 이미 따옴표로 묶은 값 안에서 따옴표를 이스케이프하는 데 사용되는 단일 문자를 설정합니다. None이 설정되어있는 경우 , \ escapeQuotes – 따옴표를 포함하는 값을 항상 따옴표로 묶어야하는지 여부를 나타내는 플래그를 사용합니다. None을 설정하면 기본값 인 true를 사용하고 따옴표 문자가 포함 된 모든 값을 이스케이프합니다. quoteAll – 모든 값을 항상 따옴표로 묶어야하는지 여부를 나타내는 플래그. None이 설정되면 기본값 인 false를 사용하고 따옴표 문자를 포함하는 이스케이프 값만 사용합니다. header – 열 이름을 첫 번째 줄로 씁니다. None이 설정되면 기본값 인 false를 사용합니다. nullValue – null 값의 문자열 표현을 설정합니다. None이 설정되면 기본값 인 빈 문자열을 사용합니다. 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.