Carregar arquivo CSV com Spark


110

Sou novo no Spark e estou tentando ler dados CSV de um arquivo com o Spark. Aqui está o que estou fazendo:

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

Eu esperaria que esta chamada me desse uma lista das duas primeiras colunas do meu arquivo, mas estou recebendo este erro:

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

embora meu arquivo CSV tenha mais de uma coluna.

Respostas:


63

Tem certeza de que todas as linhas têm pelo menos 2 colunas? Você pode tentar algo como, apenas para verificar ?:

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

Como alternativa, você pode imprimir o culpado (se houver):

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

Era isso, uma linha com apenas uma coluna, obrigado.
Kernael

2
É melhor analisar usando a csvbiblioteca integrada para lidar com todos os escapes porque simplesmente dividir por vírgula não funcionará se, digamos, houver vírgulas nos valores.
sudo de

4
Existem muitas ferramentas para analisar csv, não reinvente a roda
Stephen

2
Este código será interrompido se houver uma vírgula entre aspas. Analisar csv é mais complicado do que apenas dividir em ",".
Alceu Costa

Isso quebra por vírgulas. Isso é muito ruim.
rjurney

184

Spark 2.0.0+

Você pode usar a fonte de dados csv integrada diretamente:

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

ou

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

sem incluir quaisquer dependências externas.

Spark <2.0.0 :

Em vez de análise manual, que está longe de ser trivial em um caso geral, eu recomendaria spark-csv:

Certifique-se que CSV faísca está incluído no caminho ( --packages, --jars, --driver-class-path)

E carregue seus dados da seguinte maneira:

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

Ele pode lidar com o carregamento, inferência de esquema, eliminação de linhas malformadas e não requer a passagem de dados do Python para a JVM.

Nota :

Se você conhece o esquema, é melhor evitar a inferência do esquema e transmiti-lo DataFrameReader. Supondo que você tenha três colunas - inteiro, duplo e string:

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
Se você fizer isso, não se esqueça de incluir o pacote csv databricks ao abrir o shell pyspark ou usar o spark-submit. Por exemplo, pyspark --packages com.databricks:spark-csv_2.11:1.4.0(certifique-se de alterar as versões do databricks / spark para as que você instalou).
Galen Long de

É csvContext ou sqlContext no pyspark? Porque no scala você precisa do csvContext
Geoffrey Anderson

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

use 'sep não' separador 'da seguinte maneira: df = spark.read.csv ("/ home / stp / test1.csv", header = True, sep = "|")
Grant Shannon

18

E mais uma opção que consiste em ler o arquivo CSV usando o Pandas e depois importar o DataFrame do Pandas para o Spark.

Por exemplo:

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
Por que OP gostaria de fazer no Spark se ele é capaz de carregar dados no pandas
WoodChopper

Não querendo instalar ou especificar dependências em cada cluster do
Spark

O Panda permite a fragmentação de arquivos durante a leitura, portanto, ainda há um caso de uso aqui para que o Pandas lide com a análise inicial de arquivos. Veja minha resposta abaixo para o código.
abby sobh

Cuidado: o Pandas também lida com o esquema de coluna de maneira diferente do spark, especialmente quando há espaços em branco envolvidos. É mais seguro carregar apenas csv como strings para cada coluna.
AntiPawn79

@WoodChopper Você pode usar o Pandas como UDF no Spark, não?
flow2k

16

A simples divisão por vírgula também dividirá as vírgulas que estão dentro dos campos (por exemplo a,b,"1,2,3",c), portanto, não é recomendado. A resposta de zero323 é boa se você quiser usar a API DataFrames, mas se quiser manter a base do Spark, você pode analisar csvs em Python base com o módulo csv :

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

EDIT: Como @muon mencionou nos comentários, isso tratará o cabeçalho como qualquer outra linha, portanto, você precisará extraí-lo manualmente. Por exemplo, header = rdd.first(); rdd = rdd.filter(lambda x: x != header)(certifique-se de não modificar headerantes de o filtro avaliar). Mas, neste ponto, provavelmente é melhor usar um analisador csv integrado.


1
Você não precisa do Hive para usar DataFrames. Quanto à sua solução: a) Não há necessidade de StringIO. csvpode usar qualquer iterável b) __next__não deve ser usado diretamente e falhará na linha vazia. Dê uma olhada em flatMap c) Seria muito mais eficiente de usar em mapPartitionsvez de inicializar o leitor em cada linha :)
zero323

Muito obrigado pelas correções! Antes de editar minha resposta, quero ter certeza de que entendi completamente. 1) Por que rdd.mapPartitions(lambda x: csv.reader(x))funciona enquanto rdd.map(lambda x: csv.reader(x))lança um erro? Eu esperava que ambos jogassem o mesmo TypeError: can't pickle _csv.reader objects. Também parece que mapPartitionsautomaticamente chama algum equivalente a "readlines" no csv.readerobjeto, onde com map, eu precisei chamar __next__explicitamente para obter as listas de csv.reader. 2) Onde flatMapentra? Ligar mapPartitionssozinho funcionou para mim.
Galen Long,

1
rdd.mapPartitions(lambda x: csv.reader(x))funciona porque mapPartitionsespera um Iterableobjeto. Se você quiser ser explícito, você pode compreender ou gerar expressão. mapsozinho não funciona porque não itera sobre o objeto. Daí minha sugestão de usar o flatMap(lambda x: csv.reader([x]))que itera sobre o leitor. Mas mapPartitionsé muito melhor aqui.
zero323

1
note que isso lerá o cabeçalho como uma linha de dados, não como cabeçalho
muon

7

Isto está em PYSPARK

path="Your file path with file name"

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

Então você pode verificar

df.show(5)
df.count()

6

Se você deseja carregar csv como um dataframe, pode fazer o seguinte:

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

Funcionou bem para mim.


@GalenLong se você não se importa, você pode compartilhar a resposta já existente
Jeril

Estranho, juro que houve outra resposta com esta solução. Talvez eu tenha me confundido com outra pergunta. Foi mal.
Galen Long

5

Isso está de acordo com o que JP Mercier sugeriu inicialmente sobre o uso de Pandas, mas com uma modificação importante: se você ler dados em Pandas em blocos, eles devem ser mais maleáveis. Isso significa que você pode analisar um arquivo muito maior do que o Pandas pode realmente manipular como uma única peça e passá-lo para o Spark em tamanhos menores. (Isso também responde ao comentário sobre por que alguém gostaria de usar o Spark se eles podem carregar tudo no Pandas de qualquer maneira.)

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

Agora, também há outra opção para qualquer arquivo csv geral: https://github.com/seahboonsiew/pyspark-csv da seguinte maneira:

Suponha que temos o seguinte contexto

sc = SparkContext
sqlCtx = SQLContext or HiveContext

Primeiro, distribua pyspark-csv.py para executores usando SparkContext

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

Leia dados csv via SparkContext e converta-os em DataFrame

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

3

Se seus dados csv não contiverem novas linhas em nenhum dos campos, você pode carregar seus dados com textFile()e analisá-los

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

Se você tiver uma ou mais linha (s) com menos ou mais número de colunas do que 2 no conjunto de dados, este erro pode ocorrer.

Também sou novo no Pyspark e estou tentando ler arquivos CSV. O código a seguir funcionou para mim:

Neste código, estou usando o conjunto de dados do kaggle, o link é: https://www.kaggle.com/carrie1/ecommerce-data

1. Sem mencionar o esquema:

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

Agora verifique as colunas: sdfData.columns

A saída será:

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

Verifique o tipo de dados para cada coluna:

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

Isso fornecerá o quadro de dados com todas as colunas com tipo de dados como StringType

2. Com esquema: se você conhece o esquema ou deseja alterar o tipo de dados de qualquer coluna na tabela acima, use isso (digamos que estou tendo as seguintes colunas e as deseja em um tipo de dados específico para cada uma delas)

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)

Agora verifique o esquema para o tipo de dados de cada coluna:

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

Editado: também podemos usar a seguinte linha de código sem mencionar o esquema explicitamente:

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

O resultado é:

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

A saída será semelhante a esta:

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

Ao usar spark.read.csv, acho que usar as opções escape='"'e multiLine=Truefornecer a solução mais consistente para o padrão CSV e, em minha experiência, funciona melhor com arquivos CSV exportados do Planilhas Google.

Isso é,

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

de onde vem a centelha? é isso import pyspark as spark?
Luk Aron de

@LukAron Em um shell pyspark, sparkjá está inicializado. Em um script enviado por spark-submit, você pode instanciá-lo como from pyspark.sql import SparkSession; spark = SparkSession.builder.getOrCreate().
flow2k
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.