Як читати кілька текстових файлів в один RDD?


179

Я хочу прочитати купу текстових файлів з hdfs-місця та виконати відображення на ній в ітерації за допомогою іскри.

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

Як зазначає Нік Чаммас, це експозиція Hadoop, FileInputFormatі тому це також працює з Hadoop (і Scalding).


10
Так, це найзручніший спосіб відкрити кілька файлів як єдину RDD. API тут є лише експозицією API FileInputFormat Hadoop , тому застосовуються всі ті самі Pathпараметри.
Нік Чаммас

7
sc.wholeTextFilesзручно для даних, які не
обмежуються

1
Дивно, що якщо ви робите це і вказуєте паралелізм, скажіть, sc.textFile(multipleCommaSeparatedDirs,320)це призводить до 19430загальних завдань замість 320... він поводиться так, unionщо також призводить до шаленої кількості завдань із дуже низького паралелізму
листок

2
Нарешті я з’ясував, як працює ця відповідна зла форма файлу stackoverflow.com/a/33917492/306488, тому мені більше не потрібно розмежувати коми
листок

@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

Ви можете використовувати один текстовий виклик для читання декількох файлів. Scala:

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

5
і ідентичний синтаксис пітона
патрікссурі

8
Я думаю, що це лише синтаксис python. Еквівалент 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
    }
  }

Тепер ви отримали остаточну Уніфіковану RDD, тобто df

Необов’язково. Ви також можете перерозподілити його в одному BigRDD

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

Перерозподіл завжди працює: D


Чи це не означає, що список файлів має бути порівняно невеликим? Не мільйони файлів.
Матьє Лонгтін

2
Чи можемо ми паралелізувати операцію зчитування перелічених файлів? щось на зразок sc.parallelize?
lazywiz

1
@MathieuLongtin: Якщо ви можете застосувати виявлення розділів до свого коду Spark, тоді буде чудово ще вам потрібно зробити так само, як це. Раніше я відкривав 10k файли приблизно за хв.
Муртаза Канчвала

@lazywiz Якщо ви не хочете створити єдиний rdd, просто видаліть дію перерозподілу.
Муртаза Канчвала

3

У PySpark я знайшов додатковий корисний спосіб розбору файлів. Можливо, у Scala є еквівалент, але мені недостатньо комфортно придумати робочий переклад. Це, по суті, виклик textFile з додаванням міток (у наведеному нижче прикладі ключ = ім'я файлу, значення = 1 рядок з файлу).

"Позначений" текстFile

вхід:

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)

висновок: масив з кожним записом, що містить кортеж, використовуючи ім'я файлу як ключ і зі значенням = кожен рядок файлу. (Технічно, використовуючи цей метод, ви також можете використовувати інший ключ, крім власного імені файлового шляху - можливо, хеш-представлення для збереження на пам'яті). тобто.

[('/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, але з рядком "файл:", вилученим з файлового шляху.):

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...
Евхз

sc.wholeTextFiles ("/ path / to / dir")
Ram Ghadiyaram

1

Є прямий передній чистий розчин. Використовуйте метод wholeTextFiles (). Це займе каталог і сформує пару ключових значень. Повертається RDD буде парним RDD. Нижче описуйте документи від іскових документів :

SparkContext.wholeTextFiles дозволяє читати каталог, що містить декілька невеликих текстових файлів, і повертає кожен з них у вигляді (ім'я файлу, вмісту) пар. Це на відміну від textFile, який би повертав один запис на рядок у кожному файлі


-1

СПРОБУЙТЕ ЦЕ Інтерфейс , який використовується для написання DataFrame до зовнішніх систем зберігання даних (наприклад , файлових систем, ключ-значення магазини і т.д.). Використовуйте DataFrame.write () для доступу до цього.

Нове у версії 1.4.

csv (шлях, режим = Немає, стиснення = Ні, сеп = Немає, цитата = Ні, втеча = Ні, заголовок = Ні, nullValue = Ні, escapeQuotes = Ні, quoAll = Нічого, dateFormat = None, timestampFormat = None) Зберігає вміст DataFrame у форматі CSV у вказаному шляху.

Параметри: path - шлях у будь-якому режимі файлової системи, підтримуваної Hadoop - визначає поведінку операції збереження, коли дані вже є.

Додаток: Додайте вміст цього DataFrame до існуючих даних. перезапис: Перезапис існуючих даних. ignore: мовчки ігноруйте цю операцію, якщо дані вже є. помилка (типовий випадок): киньте виняток, якщо дані вже є. compression - компресійний кодек, який потрібно використовувати під час збереження у файлі. Це може бути одне з відомих нечутливих до регістру імен скорочень (жодне, bzip2, gzip, lz4, snappy і deflate). sep - встановлює єдиний символ як роздільник для кожного поля та значення. Якщо параметр None не встановлено, він використовує значення за замовчуванням,,. quota - встановлює єдиний символ, що використовується для уникнення значення котируваних, де роздільник може бути частиною значення. Якщо параметр None встановлений, воно використовує значення за замовчуванням, ". Якщо ви хочете вимкнути цитати, вам потрібно встановити порожній рядок. Escape - встановлює єдиний символ, що використовується для виходу з лапок всередині вже вказаного значення. Якщо значення не встановлено , воно використовує значення за замовчуванням, \ escapeQuotes - прапор, який вказує, чи слід завжди містити значення, що містять лапки. Якщо параметр None встановлений, воно використовує значення за замовчуванням true, уникаючи всіх значень, що містять символ цитати. quoteAll - Прапор, який вказує, чи всі значення завжди повинні бути укладені в лапки. Якщо параметр "None" встановлено, воно використовує значення за замовчуванням false, лише значущі значення, що містять котирування. заголовок - записує назви стовпців як перший рядок. Якщо параметр None встановлений, він використовує значення за замовчуванням, false. nullValue - встановлює рядкове подання нульового значення. Якщо параметр None встановлений, він використовує значення за замовчуванням, порожній рядок. dateFormat - встановлює рядок, що вказує на формат дати. Спеціальні формати дати дотримуйтесь форматів на java.text.SimpleDateFormat. Це стосується типу дати. Якщо параметр None не встановлений, він використовує значення за замовчуванням, yyyy-MM-dd. timestampFormat - встановлює рядок, що вказує на формат часової мітки. Спеціальні формати дати дотримуйтесь форматів на java.text.SimpleDateFormat. Це стосується типу часової позначки. Якщо параметр None не встановлений, він використовує значення за замовчуванням, yyyy-MM-dd'T'HH: mm: ss.SSSZZ.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.