Carica il file CSV con Spark


110

Sono nuovo in Spark e sto cercando di leggere i dati CSV da un file con Spark. Ecco cosa sto facendo:

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

Mi aspetto che questa chiamata mi dia un elenco delle prime due colonne del mio file, ma ricevo questo errore:

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

sebbene il mio file CSV sia più di una colonna.

Risposte:


63

Sei sicuro che tutte le righe abbiano almeno 2 colonne? Puoi provare qualcosa di simile, solo per controllare ?:

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

In alternativa, puoi stampare il colpevole (se presente):

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

Ecco fatto, una riga con una sola colonna, grazie.
Kernael

2
È meglio analizzare utilizzando la csvlibreria incorporata per gestire tutti gli escaping perché la semplice divisione per virgola non funzionerà se, ad esempio, ci sono virgole nei valori.
sudo

4
Ci sono molti strumenti per analizzare csv, non reinventare la ruota
Stephen

2
Questo codice si interromperà se c'è una virgola tra virgolette. L'analisi di csv è più complicata della semplice suddivisione in ",".
Alceu Costa

Si interrompe per le virgole. Questo è molto brutto.
rjurney

184

Spark 2.0.0+

Puoi utilizzare direttamente l'origine dati CSV incorporata:

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

o

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

senza includere eventuali dipendenze esterne.

Spark <2.0.0 :

Invece dell'analisi manuale, che è tutt'altro che banale in un caso generale, consiglierei spark-csv:

Assicurarsi che Spark CSV è incluso nel percorso ( --packages, --jars, --driver-class-path)

E carica i tuoi dati come segue:

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

Può gestire il caricamento, l'inferenza dello schema, l'eliminazione di linee non corrette e non richiede il passaggio di dati da Python alla JVM.

Nota :

Se conosci lo schema, è meglio evitare l'inferenza dello schema e passarlo a DataFrameReader. Supponendo che tu abbia tre colonne: intero, doppio e stringa:

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 lo fai, non dimenticare di includere il pacchetto csv di databricks quando apri la shell pyspark o usi spark-submit. Ad esempio, pyspark --packages com.databricks:spark-csv_2.11:1.4.0(assicurati di cambiare le versioni di databricks / spark con quelle che hai installato).
Galen Long,

È csvContext o sqlContext in pyspark? Perché in scala hai bisogno di 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())

utilizza "sep not" separator "come segue: df = spark.read.csv (" / home / stp / test1.csv ", header = True, sep =" | ")
Grant Shannon

18

E ancora un'altra opzione che consiste nel leggere il file CSV utilizzando Pandas e quindi importare Pandas DataFrame in Spark.

Per esempio:

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
Perché OP vorrebbe fare su Spark se è in grado di caricare i dati nei panda
WoodChopper

Non volendo installare o specificare dipendenze su ogni cluster Spark ....
SummerEla

Panda consente la suddivisione in blocchi di file durante la lettura, quindi c'è ancora un caso d'uso qui per far sì che Pandas gestisca l'analisi iniziale del file. Vedi la mia risposta sotto per il codice.
abby sobh

Attenzione: Pandas gestisce anche lo schema delle colonne in modo diverso da Spark, specialmente quando sono coinvolti degli spazi. Più sicuro caricare solo csv come stringhe per ogni colonna.
AntiPawn79

@WoodChopper Puoi usare Pandas come UDF in Spark, no?
flow2k

16

La semplice divisione per virgola dividerà anche le virgole che si trovano all'interno dei campi (ad esempio a,b,"1,2,3",c), quindi non è raccomandato. La risposta di zero323 è buona se si desidera utilizzare l'API DataFrames, ma se si desidera attenersi a Spark di base, è possibile analizzare csvs in Python di base con il modulo csv :

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

EDIT: come @muon menzionato nei commenti, questo tratterà l'intestazione come qualsiasi altra riga, quindi dovrai estrarla manualmente. Ad esempio, header = rdd.first(); rdd = rdd.filter(lambda x: x != header)(assicurati di non modificare headerprima che il filtro valuti). Ma a questo punto, probabilmente starai meglio usando un parser csv integrato.


1
Non è necessario che Hive utilizzi DataFrame. Per quanto riguarda la tua soluzione: a) Non ce n'è bisogno StringIO. csvpuò usare qualsiasi iterabile b) __next__non dovrebbe essere usato direttamente e fallirà su una riga vuota. Dai un'occhiata a flatMap c) Sarebbe molto più efficiente da usare mapPartitionsinvece di inizializzare il lettore su ogni riga :)
zero323

Grazie mille per le correzioni! Prima di modificare la mia risposta, voglio assicurarmi di aver compreso appieno. 1) Perché rdd.mapPartitions(lambda x: csv.reader(x))funziona mentre rdd.map(lambda x: csv.reader(x))genera un errore? Mi aspettavo che entrambi lanciassero lo stesso TypeError: can't pickle _csv.reader objects. Sembra anche che mapPartitionschiami automaticamente un equivalente di "readlines" csv.readersull'oggetto, dove con map, avevo bisogno di chiamare __next__esplicitamente per estrarre gli elenchi dal file csv.reader. 2) Da dove flatMapviene? Solo chiamare mapPartitionsda solo ha funzionato per me.
Galen Long,

1
rdd.mapPartitions(lambda x: csv.reader(x))funziona perché mapPartitionsaspetta un Iterableoggetto. Se vuoi essere esplicito potresti comprendere o generare espressioni. mapda solo non funziona perché non itera sull'oggetto. Da qui il mio suggerimento di utilizzare flatMap(lambda x: csv.reader([x]))che itererà sul lettore. Ma mapPartitionsè molto meglio qui.
zero323

1
nota che questo leggerà l'intestazione come una riga di dati, non come un'intestazione
muone

7

Questo è in PYSPARK

path="Your file path with file name"

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

Quindi puoi controllare

df.show(5)
df.count()

6

Se vuoi caricare csv come dataframe, puoi fare quanto segue:

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

Ha funzionato bene per me.


@GalenLong se non ti dispiace, puoi condividere la risposta già esistente
Jeril

Strano, giuro che c'era un'altra risposta con questa soluzione. Forse mi sono confuso con un'altra domanda. Colpa mia.
Galen Long,

5

Questo è in linea con quanto inizialmente suggerito da JP Mercier sull'utilizzo di Panda, ma con una modifica importante: se si leggono i dati in Pandas in blocchi, dovrebbe essere più malleabile. Ciò significa che puoi analizzare un file molto più grande di quanto Pandas possa effettivamente gestire come un singolo pezzo e passarlo a Spark in dimensioni più piccole. (Questo risponde anche al commento sul motivo per cui si vorrebbe usare Spark se possono caricare comunque tutto in Panda.)

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

Ora, c'è anche un'altra opzione per qualsiasi file csv generale: https://github.com/seahboonsiew/pyspark-csv come segue:

Supponiamo di avere il seguente contesto

sc = SparkContext
sqlCtx = SQLContext or HiveContext

Innanzitutto, distribuisci pyspark-csv.py agli esecutori utilizzando SparkContext

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

Leggi i dati csv tramite SparkContext e convertili in DataFrame

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

3

Se i tuoi dati CSV non contengono nuove righe in nessuno dei campi, puoi caricare i tuoi dati con textFile()e analizzarli

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 si dispone di una o più righe con un numero di colonne inferiore o superiore a 2 nel set di dati, potrebbe verificarsi questo errore.

Sono anche nuovo su Pyspark e sto cercando di leggere il file CSV. Il seguente codice ha funzionato per me:

In questo codice sto usando il set di dati di kaggle il link è: https://www.kaggle.com/carrie1/ecommerce-data

1. Senza menzionare lo schema:

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

Ora controlla le colonne: sdfData.columns

L'output sarà:

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

Controlla il tipo di dati per ogni colonna:

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

Questo darà il frame di dati con tutte le colonne con tipo di dati come StringType

2. Con schema: se conosci lo schema o desideri modificare il tipo di dati di qualsiasi colonna nella tabella sopra, usa questo (diciamo che ho le seguenti colonne e le voglio in un particolare tipo di dati per ciascuna di esse)

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)

Ora controlla lo schema per il tipo di dati di ciascuna colonna:

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

Modificato: possiamo usare anche la seguente riga di codice senza menzionare esplicitamente lo schema:

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

L'output è:

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

L'output sarà simile a questo:

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

Durante l'utilizzo spark.read.csv, trovo che l'utilizzo delle opzioni escape='"'e multiLine=Truefornisca la soluzione più coerente allo standard CSV e, nella mia esperienza, funziona al meglio con i file CSV esportati da Fogli Google.

Questo è,

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

da dove viene la scintilla? è vero import pyspark as spark?
Luk Aron

@LukAron In una shell pyspark, sparkè già inizializzato. In uno script inviato da spark-submit, puoi istanziarlo come from pyspark.sql import SparkSession; spark = SparkSession.builder.getOrCreate().
flow2k
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.