Tek bir RDD'de birden çok metin dosyası nasıl okunur?


179

Bir hdfs konumundan bir sürü metin dosyasını okumak ve kıvılcım kullanarak bir yinelemede eşleme yapmak istiyorum.

JavaRDD<String> records = ctx.textFile(args[1], 1); aynı anda yalnızca bir dosyayı okuyabilir.

Birden fazla dosyayı okumak ve bunları tek bir RDD olarak işlemek istiyorum. Nasıl?

Yanıtlar:


299

Tüm dizinleri belirleyebilir, joker karakterler ve hatta dizinlerin ve joker karakterlerin CSV'sini kullanabilirsiniz. Örneğin:

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

Nick Chammas'ın belirttiği gibi, bu Hadoop'ların bir maruziyeti FileInputFormatve bu nedenle Hadoop (ve Scalding) ile de çalışıyor.


10
Evet, bu, birden fazla dosyayı tek bir RDD olarak açmanın en uygun yoludur. Buradaki API, Hadoop'un FileInputFormat API'sının yalnızca bir gösterimidir , bu nedenle aynı Pathseçenekler geçerlidir.
Nick Chammas

7
sc.wholeTextFilessınırlandırılmamış veriler için kullanışlıdır
Michal Čizmazia

1
Yine de bunu yaparsanız ve paralellik belirtirseniz, bunun yerine toplam görevlere sc.textFile(multipleCommaSeparatedDirs,320)yol açtığını söyleyin ... çok düşük paralellikten deli sayıda 19430320union
göreve

2
Sonunda bu kötü dosya deseni eşleşmesinin nasıl çalıştığını buldum stackoverflow.com/a/33917492/306488 Artık virgül
ayırmaya

@femibyte Ben öyle düşünmüyorum, ancak neden dosya adını neden dışında herhangi bir durumda bilmek istediğinizi bilmiyorum wholeTextFiles. Kullanım durumunuz nedir? Dosyalarla aynı sayıda bölüm kullanmanız koşuluyla bir çözüm düşünebilirim ...
samthebest

35

unionAşağıdaki gibi kullanın :

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

Sonra bigRddtüm dosyaları ile RDD olduğunu.


Teşekkür ederim bulut, bu şekilde istediğim tüm dosyaları okuyabilirim, ama bir tane! Ama yine de, bir çok şey yazmak zorundayım ...
gsamaras

30

Birden çok dosyayı okumak için tek bir textFile çağrısı kullanabilirsiniz. Scala:

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

5
ve özdeş python sözdizimi
patricksurry

8
Bence bu sadece python sözdizimi. Scala eşdeğerisc.textFile(files.mkString(","))
Davos

9

Bunu kullanabilirsiniz

İlk önce bir Tampon / S3 Yolları Listesi alabilirsiniz:

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
  }

Şimdi bu List nesnesini aşağıdaki kod parçasına iletin, not: sc SQLContext nesnesidir

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

Şimdi son bir Birleşik RDD var yani df

İsteğe bağlı, Ve ayrıca tek bir BigRDD'de yeniden bölümlendirebilirsiniz

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

Yeniden bölümleme her zaman çalışır: D


Bu, dosya listesinin nispeten küçük olması gerektiği anlamına gelmiyor mu? Milyonlarca dosya değil.
Mathieu Longtin

2
Listelenen dosyaları okuma işlemini paralel hale getirebilir miyiz? sc.parallelize gibi bir şey?
lazywiz

1
@MathieuLongtin: Spark kodunuza bölüm keşfi uygulayabiliyorsanız, aynı şeyi yapmanız gerekir. 10k dosyaları yaklaşık bir dakika içinde açardım.
Murtaza Kanchwala

@ lazywiz Tek bir rdd oluşturmak istemiyorsanız, yeniden bölümlendirme eylemini kaldırın.
Murtaza Kanchwala

3

PySpark'ta dosyaları ayrıştırmak için ek bir yararlı yol buldum. Belki de Scala'da bir eşdeğeri var, ama çalışan bir çeviri bulmakta yeterince rahat değilim. Aslında, etiketlerin eklenmesiyle bir textFile çağrısıdır (aşağıdaki örnekte anahtar = dosyaadı, değer = dosyadan 1 satır).

"Etiketli" textFile

giriş:

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)

output: her girdinin anahtar olarak dosyaadı kullanarak bir demet içeren ve value = her dosya satırı ile dizi. (Teknik olarak, bu yöntemi kullanarak, gerçek dosya yolu adının yanı sıra belki de belleğe kaydetmek için karma bir temsil dışında farklı bir anahtar da kullanabilirsiniz). yani.

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

Ayrıca bir satır listesi olarak da yeniden birleştirebilirsiniz:

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

Veya tüm dosyaları tek bir dizeye geri birleştirin (bu örnekte sonuç wholeTextFiles ile elde ettiğinizle aynıdır, ancak "file:" dizesi dosya yolundan çıkarılmıştır.):

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


Ben kod bu satır koştu - Spark_Full += sc.textFile(filename).keyBy(lambda x: filename) yani hata var TypeError: 'PipelinedRDD' object is not iterable. Anladığım kadarıyla, bu çizgi değişmez bir RDD oluşturur, bu yüzden onu başka bir değişkene nasıl ekleyebileceğinizi merak ediyordum?
KartikKannapur

3

kullanabilirsiniz

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

burada dosyanızın yolunu ve bu dosyanın içeriğini göreceksiniz. böylece, tüm dosyayı herhangi bir eylemi yükü koruyan bir zamanda gerçekleştirebilirsiniz


2

Tüm cevaplar doğru sc.textFile

Sadece neden olmasın diye merak ediyordum wholeTextFilesÖrneğin, bu durumda ...

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

bir sınırlama, küçük dosyaları yüklemek zorundayız, aksi takdirde performans kötü olur ve OOM'ye yol açabilir.

Not :

  • Tüm dosya belleğe sığmalıdır
  • XML dosyaları gibi satırlara bölünemeyen dosya biçimleri için iyi ...

Ziyaret için daha fazla referans


ya da sadecesc.wholeTextFiles(folder).flatMap...
Evhz

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

1

Düz bir temiz çözelti mevcuttur. WholeTextFiles () yöntemini kullanın. Bu bir dizin alır ve bir anahtar değer çifti oluşturur. Döndürülen RDD, bir çift RDD olacaktır. Spark belgelerinden aşağıdaki açıklamaları bulabilirsiniz :

SparkContext.wholeTextFiles, birden çok küçük metin dosyası içeren bir dizini okumanızı ve her birini (dosya adı, içerik) çiftleri olarak döndürmenizi sağlar. Bu, her dosyada satır başına bir kayıt döndürecek olan textFile ile zıttır.


-1

Harici depolama sistemlerine (örn. Dosya sistemleri, anahtar / değer depoları, vb.) Bir DataFrame yazmak için kullanılan BU ARAYI DENEYİN. Buna erişmek için DataFrame.write () öğesini kullanın.

1.4 sürümündeki yenilikler.

csv (yol, mod = Yok, sıkıştırma = Yok, sep = Yok, alıntı = Yok, kaçış = Yok, başlık = Yok, nullValue = Yok, escapeQuotes = Yok, quoteAll = Yok, dateFormat = Yok, timestampFormat = Yok) DataFrame içeriğinin belirtilen yoldaki CSV biçiminde içeriği.

Parametreler: path - herhangi bir Hadoop destekli dosya sistemi modundaki yol - veriler zaten mevcut olduğunda kaydetme işleminin davranışını belirtir.

append: Bu DataFrame'in içeriğini mevcut verilere ekleyin. üzerine yaz: Mevcut verilerin üzerine yaz. yoksay: Veriler zaten mevcutsa bu işlemi sessizce yoksay. error (varsayılan durum): Veriler zaten mevcutsa bir istisna atar. sıkıştırma - dosyaya kaydederken kullanılacak sıkıştırma codec bileşeni. Bu, büyük / küçük harfe duyarlı olmayan kısaltma adlarından biri olabilir (yok, bzip2, gzip, lz4, çabuk ve sönük). sep - tek karakteri her alan ve değer için ayırıcı olarak ayarlar. None (Hiçbiri) ayarlanırsa, varsayılan değer olan `` değerini kullanır. quote - Ayırıcının değerin bir parçası olabileceği tırnak içindeki değerlerden kaçmak için kullanılan tek karakteri ayarlar. Hiçbiri ayarlanırsa, varsayılan değer olan "" değerini kullanır. Teklifleri kapatmak istiyorsanız, boş bir dize ayarlamanız gerekir. , varsayılan değeri kullanır, \ escapeQuotes - Tırnak içeren değerlerin her zaman tırnak içine alınması gerekip gerekmediğini gösteren bir işaret. Hiçbiri ayarlanırsa, bir tırnak karakteri içeren tüm değerlerden kaçarak varsayılan true değerini kullanır. quoteAll - Tüm değerlerin her zaman tırnak işaretleri içine alınması gerekip gerekmediğini gösteren bir işaret. None (Hiçbiri) ayarlanırsa, varsayılan değer false değerini kullanır, yalnızca tırnak işareti içeren değerleri kullanır. başlık - sütunların adlarını ilk satır olarak yazar. None (Hiçbiri) ayarlanırsa, varsayılan false değerini kullanır. nullValue - null değerin dize olarak temsilini ayarlar. None (Hiçbiri) ayarlanırsa, varsayılan değer olan boş dize kullanır. dateFormat - bir tarih biçimini gösteren dizeyi ayarlar. Özel tarih biçimleri java.text.SimpleDateFormat adresindeki biçimleri izler. Bu, tarih türü için geçerlidir. None (Hiçbiri) ayarlanırsa, yyyy-AA-gg varsayılan değer değerini kullanır. timestampFormat - zaman damgası biçimini belirten dizeyi ayarlar. Özel tarih biçimleri java.text.SimpleDateFormat adresindeki biçimleri izler. Bu, zaman damgası türü için geçerlidir. None (Hiçbiri) ayarlanırsa, yyyy-AA-gg'T'HH: aa: ss.SSSZZ varsayılan değer değerini kullanır.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.