Как прочитать несколько текстовых файлов в одном RDD?


179

Я хочу прочитать несколько текстовых файлов из местоположения hdfs и выполнить сопоставление с ним в итерации, используя spark.

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это СДР со всеми файлами.


Спасибо, облако, таким образом я могу читать все файлы, которые я хочу, но один! Но все же, я должен написать много вещей ...
gsamaras

30

Вы можете использовать один вызов textFile для чтения нескольких файлов. Scala:

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

5
и идентичный синтаксис python
patricksurry

8
Я думаю, что это только синтаксис Python. Эквивалент Scala будетsc.textFile(files.mkString(","))
Давос

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

Теперь вы получили окончательный Унифицированный СДР, т.е. 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 строка из файла).

«Помеченный» 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)

output: массив с каждой записью, содержащей кортеж, используя 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...
евхз

sc.wholeTextFiles («/ path / to / dir»)
Рам Гадиярам

1

Есть прямое чистое доступное решение. Используйте метод wholeTextFiles (). Это займет каталог и сформирует пару ключ-значение. Возвращенная СДР будет парой СДР. Найдите ниже описание из документов Spark :

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


-1

ПОПРОБУЙТЕ ЭТОТ интерфейс, используемый для записи DataFrame во внешние системы хранения (например, файловые системы, хранилища ключей и т. Д.). Используйте DataFrame.write () для доступа к этому.

Новое в версии 1.4.

csv (путь, режим = Нет, сжатие = Нет, септ = Нет, кавычка = Нет, escape = Нет, заголовок = Нет, nullValue = Нет, escapeQuotes = Нет, quoteAll = Нет, dateFormat = Нет, timestampFormat = Нет) Сохраняет содержимое DataFrame в формате CSV по указанному пути.

Параметры: путь - путь в любом режиме файловой системы, поддерживаемом Hadoop, - определяет поведение операции сохранения, когда данные уже существуют.

append: добавить содержимое этого DataFrame к существующим данным. перезаписать: перезаписать существующие данные. игнорировать: игнорировать эту операцию, если данные уже существуют. ошибка (случай по умолчанию): выдает исключение, если данные уже существуют. сжатие - кодек сжатия, используемый при сохранении в файл. Это может быть одно из известных сокращенных имен без учета регистра (none, bzip2, gzip, lz4, snappy и deflate). sep - устанавливает отдельный символ в качестве разделителя для каждого поля и значения. Если None установлен, он использует значение по умолчанию,. цитата - устанавливает единственный символ, используемый для экранирования значений в кавычках, где разделитель может быть частью значения. Если None установлен, он использует значение по умолчанию ". Если вы хотите отключить кавычки, вам нужно установить пустую строку. Escape - устанавливает единственный символ, используемый для экранирования кавычек внутри уже заключенного в кавычки значения. Если None установлен , он использует значение по умолчанию, \ escapeQuotes - флаг, указывающий, должны ли значения, содержащие кавычки, всегда заключаться в кавычки. Если None установлен, он использует значение по умолчанию true, экранируя все значения, содержащие символ кавычки. quoteAll - Флаг, указывающий, должны ли все значения всегда заключаться в кавычки. Если None установлен, он использует значение по умолчанию false, только экранируя значения, содержащие символ кавычки. header - записывает имена столбцов в качестве первой строки. Если 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.