Загрузите файл CSV с помощью Spark


110

Я новичок в Spark, и я пытаюсь прочитать данные CSV из файла с помощью Spark. Вот что я делаю:

sc.textFile('file.csv')
    .map(lambda line: (line.split(',')[0], line.split(',')[1]))
    .collect()

Я ожидал, что этот вызов даст мне список двух первых столбцов моего файла, но я получаю эту ошибку:

File "<ipython-input-60-73ea98550983>", line 1, in <lambda>
IndexError: list index out of range

хотя в моем CSV-файле больше одного столбца.

Ответы:


63

Вы уверены, что во всех строках хотя бы 2 столбца? Можете попробовать что-нибудь вроде, просто чтобы проверить ?:

sc.textFile("file.csv") \
    .map(lambda line: line.split(",")) \
    .filter(lambda line: len(line)>1) \
    .map(lambda line: (line[0],line[1])) \
    .collect()

В качестве альтернативы вы можете указать виновника (если есть):

sc.textFile("file.csv") \
    .map(lambda line: line.split(",")) \
    .filter(lambda line: len(line)<=1) \
    .collect()

Вот и все, одна строка только с одним столбцом, спасибо.
Kernael

2
Лучше использовать встроенную csvбиблиотеку для обработки всех экранирований, потому что простое разделение запятой не сработает, если, скажем, в значениях есть запятые.
sudo

4
Есть много инструментов для синтаксического анализа csv, не изобретайте велосипед
Стивен

2
Этот код сломается, если в кавычках стоит запятая. Парсинг csv более сложен, чем просто разделение на ",".
Alceu Costa

Это разрыв для запятых. Это очень плохо.
rjurney

184

Spark 2.0.0+

Вы можете напрямую использовать встроенный источник данных csv:

spark.read.csv(
    "some_input_file.csv", header=True, mode="DROPMALFORMED", schema=schema
)

или

(spark.read
    .schema(schema)
    .option("header", "true")
    .option("mode", "DROPMALFORMED")
    .csv("some_input_file.csv"))

без включения каких-либо внешних зависимостей.

Spark <2.0.0 :

Вместо ручного синтаксического анализа, который в общем случае далеко не тривиален, я бы рекомендовал spark-csv:

Убедитесь в том , что Спарк CSV включен в пути ( --packages, --jars, --driver-class-path)

И загрузите свои данные следующим образом:

(df = sqlContext
    .read.format("com.databricks.spark.csv")
    .option("header", "true")
    .option("inferschema", "true")
    .option("mode", "DROPMALFORMED")
    .load("some_input_file.csv"))

Он может обрабатывать загрузку, вывод схемы, отбрасывание искаженных строк и не требует передачи данных из Python в JVM.

Примечание :

Если вы знаете схему, лучше избегать вывода схемы и передавать ее DataFrameReader. Предположим, у вас есть три столбца - целые, двойные и строковые:

from pyspark.sql.types import StructType, StructField
from pyspark.sql.types import DoubleType, IntegerType, StringType

schema = StructType([
    StructField("A", IntegerType()),
    StructField("B", DoubleType()),
    StructField("C", StringType())
])

(sqlContext
    .read
    .format("com.databricks.spark.csv")
    .schema(schema)
    .option("header", "true")
    .option("mode", "DROPMALFORMED")
    .load("some_input_file.csv"))

6
Если вы это сделаете, не забудьте включить csv-пакет databricks, когда вы открываете оболочку pyspark или используете spark-submit. Например, pyspark --packages com.databricks:spark-csv_2.11:1.4.0(не забудьте изменить версии databricks / spark на те, которые вы установили).
Гален Лонг,

Это csvContext или sqlContext в pyspark? Потому что в scala вам нужен csvContext
Джеффри Андерсон

28
from pyspark.sql import SparkSession

spark = SparkSession \
    .builder \
    .appName("Python Spark SQL basic example") \
    .config("spark.some.config.option", "some-value") \
    .getOrCreate()

df = spark.read.csv("/home/stp/test1.csv",header=True,sep="|");

print(df.collect())

используйте разделитель 'sep not', как показано ниже: df = spark.read.csv ("/ home / stp / test1.csv", header = True, sep = "|")
Грант Шеннон

18

И еще один вариант, который заключается в чтении файла CSV с помощью Pandas, а затем импорте Pandas DataFrame в Spark.

Например:

from pyspark import SparkContext
from pyspark.sql import SQLContext
import pandas as pd

sc = SparkContext('local','example')  # if using locally
sql_sc = SQLContext(sc)

pandas_df = pd.read_csv('file.csv')  # assuming the file contains a header
# pandas_df = pd.read_csv('file.csv', names = ['column 1','column 2']) # if no header
s_df = sql_sc.createDataFrame(pandas_df)

7
Зачем OP использовать искру, если он может загружать данные в
пандах


Panda позволяет разбивать файлы на части при чтении, поэтому здесь все еще есть вариант использования, когда Pandas обрабатывает начальный анализ файла. Смотрите мой ответ ниже для кода.
Эбби Собх 06

Внимание: Pandas также обрабатывает схему столбцов иначе, чем Spark, особенно когда есть пробелы. Безопаснее просто загружать csv в виде строк для каждого столбца.
AntiPawn79 02

@WoodChopper Вы можете использовать Pandas в качестве UDF в Spark, не так ли?
flow2k

16

Простое разделение запятыми также приведет к разделению запятых внутри полей (например a,b,"1,2,3",c), поэтому это не рекомендуется. Ответ zero323 хорош, если вы хотите использовать API DataFrames, но если вы хотите придерживаться базового Spark, вы можете проанализировать csvs в базовом Python с помощью модуля csv :

# works for both python 2 and 3
import csv
rdd = sc.textFile("file.csv")
rdd = rdd.mapPartitions(lambda x: csv.reader(x))

РЕДАКТИРОВАТЬ: как @muon упомянул в комментариях, это будет обрабатывать заголовок как любую другую строку, поэтому вам нужно будет извлечь его вручную. Например, header = rdd.first(); rdd = rdd.filter(lambda x: x != header)(убедитесь, что не изменили headerдо оценки фильтра). Но на этом этапе вам, вероятно, лучше использовать встроенный парсер csv.


1
Для использования DataFrames не требуется Hive. Касательно вашего решения: а) Нет необходимости StringIO. csvможет использовать любую итерацию b) __next__не должен использоваться напрямую и завершится ошибкой в ​​пустой строке. Взгляните на flatMap c) Было бы гораздо эффективнее использовать mapPartitionsвместо инициализации считывателя на каждой строке :)
zero323

Большое спасибо за исправления! Прежде чем редактировать свой ответ, я хочу убедиться, что полностью понимаю. 1) Почему rdd.mapPartitions(lambda x: csv.reader(x))срабатывает, пока rdd.map(lambda x: csv.reader(x))выдает ошибку? Я ожидал, что оба бросят одинаково TypeError: can't pickle _csv.reader objects. Также кажется, что mapPartitionsавтоматически вызывает некоторый эквивалент "readlines" для csv.readerобъекта, где with map, мне нужно было __next__явно вызвать, чтобы получить списки из csv.reader. 2) Откуда тут flatMapвзяться? Просто позвонить в mapPartitionsодиночку сработало для меня.
Гален Лонг,

1
rdd.mapPartitions(lambda x: csv.reader(x))работает, потому что mapPartitionsожидает Iterableобъект. Если вы хотите быть явным, вы могли бы понять понимание или выражение генератора. mapсам по себе не работает, потому что он не выполняет итерацию по объекту. Отсюда и мое предложение использовать, flatMap(lambda x: csv.reader([x]))который будет перебирать читателя. Но mapPartitionsздесь намного лучше.
zero323

1
обратите внимание, что это будет читать заголовок как строку данных, а не как заголовок
мюон

7

Это в PYSPARK

path="Your file path with file name"

df=spark.read.format("csv").option("header","true").option("inferSchema","true").load(path)

Тогда вы можете проверить

df.show(5)
df.count()

6

Если вы хотите загрузить csv как фрейм данных, вы можете сделать следующее:

from pyspark.sql import SQLContext
sqlContext = SQLContext(sc)

df = sqlContext.read.format('com.databricks.spark.csv') \
    .options(header='true', inferschema='true') \
    .load('sampleFile.csv') # this is your csv file

У меня все сработало.


@GalenLong, если вы не против, можете ли вы поделиться уже существующим ответом
Джерил,

Странно, клянусь, у этого решения был еще один ответ. Может я перепутал это с другим вопросом. Виноват.
Гален Лонг

5

Это соответствует тому, что изначально предлагал JP Mercier об использовании Pandas, но с серьезной модификацией: если вы читаете данные в Pandas кусками, они должны быть более гибкими. Это означает, что вы можете анализировать файл гораздо большего размера, чем Pandas может обрабатывать как единый фрагмент, и передавать его Spark в меньших размерах. (Это также отвечает на комментарий о том, почему нужно использовать Spark, если они все равно могут загружать все в Pandas.)

from pyspark import SparkContext
from pyspark.sql import SQLContext
import pandas as pd

sc = SparkContext('local','example')  # if using locally
sql_sc = SQLContext(sc)

Spark_Full = sc.emptyRDD()
chunk_100k = pd.read_csv("Your_Data_File.csv", chunksize=100000)
# if you have headers in your csv file:
headers = list(pd.read_csv("Your_Data_File.csv", nrows=0).columns)

for chunky in chunk_100k:
    Spark_Full +=  sc.parallelize(chunky.values.tolist())

YourSparkDataFrame = Spark_Full.toDF(headers)
# if you do not have headers, leave empty instead:
# YourSparkDataFrame = Spark_Full.toDF()
YourSparkDataFrame.show()

5

Теперь есть еще один вариант для любого общего файла csv: https://github.com/seahboonsiew/pyspark-csv следующим образом:

Предположим, у нас есть следующий контекст

sc = SparkContext
sqlCtx = SQLContext or HiveContext

Сначала раздайте pyspark-csv.py исполнителям с помощью SparkContext

import pyspark_csv as pycsv
sc.addPyFile('pyspark_csv.py')

Чтение данных csv через SparkContext и преобразование их в DataFrame

plaintext_rdd = sc.textFile('hdfs://x.x.x.x/blah.csv')
dataframe = pycsv.csvToDataFrame(sqlCtx, plaintext_rdd)

3

Если ваши данные csv не содержат символов новой строки ни в одном из полей, вы можете загрузить свои данные textFile()и проанализировать их.

import csv
import StringIO

def loadRecord(line):
    input = StringIO.StringIO(line)
    reader = csv.DictReader(input, fieldnames=["name1", "name2"])
    return reader.next()

input = sc.textFile(inputFile).map(loadRecord)

2

Если у вас есть одна или несколько строк с меньшим или большим количеством столбцов, чем 2 в наборе данных, может возникнуть эта ошибка.

Я также новичок в Pyspark и пытаюсь прочитать файл CSV. Для меня работал следующий код:

В этом коде я использую набор данных из kaggle, ссылка: https://www.kaggle.com/carrie1/ecommerce-data

1. Без упоминания схемы:

from pyspark.sql import SparkSession  
scSpark = SparkSession \
    .builder \
    .appName("Python Spark SQL basic example: Reading CSV file without mentioning schema") \
    .config("spark.some.config.option", "some-value") \
    .getOrCreate()

sdfData = scSpark.read.csv("data.csv", header=True, sep=",")
sdfData.show()

Теперь проверьте столбцы: sdfData.columns

Результат будет:

['InvoiceNo', 'StockCode','Description','Quantity', 'InvoiceDate', 'CustomerID', 'Country']

Проверьте тип данных для каждого столбца:

sdfData.schema
StructType(List(StructField(InvoiceNo,StringType,true),StructField(StockCode,StringType,true),StructField(Description,StringType,true),StructField(Quantity,StringType,true),StructField(InvoiceDate,StringType,true),StructField(UnitPrice,StringType,true),StructField(CustomerID,StringType,true),StructField(Country,StringType,true)))

Это даст фрейм данных со всеми столбцами с типом данных как StringType

2. Со схемой: если вы знаете схему или хотите изменить тип данных любого столбца в приведенной выше таблице, используйте это (скажем, у меня есть следующие столбцы и я хочу, чтобы они были с определенным типом данных для каждого из них)

from pyspark.sql import SparkSession  
from pyspark.sql.types import StructType, StructField
from pyspark.sql.types import DoubleType, IntegerType, StringType
    schema = StructType([\
        StructField("InvoiceNo", IntegerType()),\
        StructField("StockCode", StringType()), \
        StructField("Description", StringType()),\
        StructField("Quantity", IntegerType()),\
        StructField("InvoiceDate", StringType()),\
        StructField("CustomerID", DoubleType()),\
        StructField("Country", StringType())\
    ])

scSpark = SparkSession \
    .builder \
    .appName("Python Spark SQL example: Reading CSV file with schema") \
    .config("spark.some.config.option", "some-value") \
    .getOrCreate()

sdfData = scSpark.read.csv("data.csv", header=True, sep=",", schema=schema)

Теперь проверьте схему на тип данных каждого столбца:

sdfData.schema

StructType(List(StructField(InvoiceNo,IntegerType,true),StructField(StockCode,StringType,true),StructField(Description,StringType,true),StructField(Quantity,IntegerType,true),StructField(InvoiceDate,StringType,true),StructField(CustomerID,DoubleType,true),StructField(Country,StringType,true)))

Отредактировано: мы также можем использовать следующую строку кода без явного упоминания схемы:

sdfData = scSpark.read.csv("data.csv", header=True, inferSchema = True)
sdfData.schema

Результат:

StructType(List(StructField(InvoiceNo,StringType,true),StructField(StockCode,StringType,true),StructField(Description,StringType,true),StructField(Quantity,IntegerType,true),StructField(InvoiceDate,StringType,true),StructField(UnitPrice,DoubleType,true),StructField(CustomerID,IntegerType,true),StructField(Country,StringType,true)))

Результат будет выглядеть так:

sdfData.show()

+---------+---------+--------------------+--------+--------------+----------+-------+
|InvoiceNo|StockCode|         Description|Quantity|   InvoiceDate|CustomerID|Country|
+---------+---------+--------------------+--------+--------------+----------+-------+
|   536365|   85123A|WHITE HANGING HEA...|       6|12/1/2010 8:26|      2.55|  17850|
|   536365|    71053| WHITE METAL LANTERN|       6|12/1/2010 8:26|      3.39|  17850|
|   536365|   84406B|CREAM CUPID HEART...|       8|12/1/2010 8:26|      2.75|  17850|
|   536365|   84029G|KNITTED UNION FLA...|       6|12/1/2010 8:26|      3.39|  17850|
|   536365|   84029E|RED WOOLLY HOTTIE...|       6|12/1/2010 8:26|      3.39|  17850|
|   536365|    22752|SET 7 BABUSHKA NE...|       2|12/1/2010 8:26|      7.65|  17850|
|   536365|    21730|GLASS STAR FROSTE...|       6|12/1/2010 8:26|      4.25|  17850|
|   536366|    22633|HAND WARMER UNION...|       6|12/1/2010 8:28|      1.85|  17850|
|   536366|    22632|HAND WARMER RED P...|       6|12/1/2010 8:28|      1.85|  17850|
|   536367|    84879|ASSORTED COLOUR B...|      32|12/1/2010 8:34|      1.69|  13047|
|   536367|    22745|POPPY'S PLAYHOUSE...|       6|12/1/2010 8:34|       2.1|  13047|
|   536367|    22748|POPPY'S PLAYHOUSE...|       6|12/1/2010 8:34|       2.1|  13047|
|   536367|    22749|FELTCRAFT PRINCES...|       8|12/1/2010 8:34|      3.75|  13047|
|   536367|    22310|IVORY KNITTED MUG...|       6|12/1/2010 8:34|      1.65|  13047|
|   536367|    84969|BOX OF 6 ASSORTED...|       6|12/1/2010 8:34|      4.25|  13047|
|   536367|    22623|BOX OF VINTAGE JI...|       3|12/1/2010 8:34|      4.95|  13047|
|   536367|    22622|BOX OF VINTAGE AL...|       2|12/1/2010 8:34|      9.95|  13047|
|   536367|    21754|HOME BUILDING BLO...|       3|12/1/2010 8:34|      5.95|  13047|
|   536367|    21755|LOVE BUILDING BLO...|       3|12/1/2010 8:34|      5.95|  13047|
|   536367|    21777|RECIPE BOX WITH M...|       4|12/1/2010 8:34|      7.95|  13047|
+---------+---------+--------------------+--------+--------------+----------+-------+
only showing top 20 rows

1

При использовании spark.read.csvя считаю, что с помощью опций escape='"'и multiLine=Trueпредоставляю наиболее согласованное решение для стандарта CSV , и в моем опыте работы лучше всего с CSV файлов , экспортированных из Google Таблиц.

То есть,

#set inferSchema=False to read everything as string
df = spark.read.csv("myData.csv", escape='"', multiLine=True,
     inferSchema=False, header=True)

откуда искра? это import pyspark as spark?
Luk Aron

@LukAron В оболочке pyspark sparkуже инициализирован. В сценарии, представленном пользователем spark-submit, вы можете создать его экземпляр как from pyspark.sql import SparkSession; spark = SparkSession.builder.getOrCreate().
flow2k
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.