Załaduj plik CSV za pomocą Sparka

110

Jestem nowy w Spark i próbuję odczytać dane CSV z pliku za pomocą Spark. Oto co robię:

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

Spodziewałbym się, że to wywołanie da mi listę dwóch pierwszych kolumn mojego pliku, ale otrzymuję ten błąd:

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

chociaż mój plik CSV ma więcej niż jedną kolumnę.

Kernael
źródło

Odpowiedzi:

63

Czy na pewno wszystkie linie mają co najmniej 2 kolumny? Czy możesz spróbować czegoś takiego, żeby sprawdzić ?:

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

Alternatywnie możesz wydrukować winowajcę (jeśli istnieje):

sc.textFile("file.csv") \
    .map(lambda line: line.split(",")) \
    .filter(lambda line: len(line)<=1) \
    .collect()
G Quintana
źródło
To było to, jeden wiersz z tylko jedną kolumną, dziękuję.
Kernael
2
Lepiej przeanalizować za pomocą wbudowanej csvbiblioteki do obsługi wszystkich znaków ucieczki, ponieważ zwykłe dzielenie przecinkiem nie zadziała, jeśli, powiedzmy, w wartościach znajdują się przecinki.
sudo
4
Jest wiele narzędzi do analizowania csv, nie odkrywaj na nowo koła
Stephen
2
Ten kod zepsuje się, jeśli w cudzysłowie znajduje się przecinek. Przetwarzanie pliku csv jest bardziej skomplikowane niż zwykłe dzielenie na ",".
Alceu Costa,
To przerywa na przecinki. To jest bardzo złe.
rjurney
184

Spark 2.0.0+

Możesz bezpośrednio użyć wbudowanego źródła danych csv:

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

lub

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

bez uwzględniania zależności zewnętrznych.

Spark <2.0.0 :

Zamiast ręcznego parsowania, co w ogólnym przypadku wcale nie jest trywialne, radziłbym spark-csv:

Upewnij się, że Spark CSV jest wliczone w ścieżce ( --packages, --jars, --driver-class-path)

I załaduj swoje dane w następujący sposób:

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

Obsługuje ładowanie, wnioskowanie o schemacie, porzucanie źle sformułowanych wierszy i nie wymaga przekazywania danych z języka Python do maszyny JVM.

Uwaga :

Jeśli znasz schemat, lepiej unikać wnioskowania o schemacie i przekazać go do DataFrameReader. Zakładając, że masz trzy kolumny - integer, double i 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"))
zero323
źródło
6
Jeśli to zrobisz, nie zapomnij dołączyć pakietu csv databricks podczas otwierania powłoki pyspark lub używania funkcji spark-submit. Na przykład pyspark --packages com.databricks:spark-csv_2.11:1.4.0(upewnij się, że zmieniłeś wersje databricks / spark na te, które masz zainstalowane).
Galen Long
Czy jest to csvContext czy sqlContext w pyspark? Ponieważ w scali potrzebujesz 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())
y durga prasad
źródło
użyj 'sep, a nie' separator 'w następujący sposób: df = spark.read.csv ("/ home / stp / test1.csv", header = True, sep = "|")
Grant Shannon
18

I jeszcze jedna opcja polegająca na odczytaniu pliku CSV za pomocą Pandas, a następnie zaimportowaniu Pandas DataFrame do Spark.

Na przykład:

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)
JP Mercier
źródło
7
Dlaczego OP miałby robić na
iskrze,
Nie chcę instalować ani określać zależności na każdym klastrze iskrowym ...
SummerEla
Panda pozwala na fragmentowanie plików podczas czytania, więc nadal istnieje przypadek użycia, aby Pandy zajmowały się początkową analizą plików. Zobacz moją odpowiedź poniżej na kod.
abby sobh
Uwaga: Pandy również radzą sobie ze schematem kolumn w inny sposób niż iskra, zwłaszcza gdy w grę wchodzą spacje. Bezpieczniej jest po prostu załadować plik csv jako ciągi dla każdej kolumny.
AntiPawn79
@WoodChopper Możesz używać Pand jako UDF w Spark, prawda?
flow2k
16

Zwykłe dzielenie przecinkiem spowoduje również podzielenie przecinków, które znajdują się w polach (np. a,b,"1,2,3",c), Więc nie jest zalecane. Odpowiedź zero323 jest dobra, jeśli chcesz używać API DataFrames, ale jeśli chcesz trzymać się podstawowego Spark, możesz przeanalizować csv w podstawowym Pythonie za pomocą modułu csv :

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

EDYCJA: Jak wspomniał @muon w komentarzach, potraktuje to nagłówek jak każdy inny wiersz, więc musisz go wyodrębnić ręcznie. Na przykład header = rdd.first(); rdd = rdd.filter(lambda x: x != header)(pamiętaj, aby nie modyfikować, headerzanim filtr oceni). Ale w tym momencie prawdopodobnie lepiej będzie, jeśli użyjesz wbudowanego parsera csv.

Galen Long
źródło
1
Nie potrzebujesz Hive, aby używać DataFrames. Jeśli chodzi o Twoje rozwiązanie: a) Nie ma takiej potrzeby StringIO. csvmoże używać dowolnej iterowalnej b) __next__nie powinno być używane bezpośrednio i zakończy się niepowodzeniem w pustym wierszu. Spójrz na flatMap c) Byłoby o wiele bardziej efektywne w użyciu mapPartitionszamiast inicjowania czytelnika na każdej linii :)
zero323
Dziękuję bardzo za poprawki! Zanim zmienię odpowiedź, chcę się upewnić, że w pełni ją rozumiem. 1) Dlaczego rdd.mapPartitions(lambda x: csv.reader(x))działa, gdy rdd.map(lambda x: csv.reader(x))zgłasza błąd? Spodziewałem się, że obaj rzucą to samo TypeError: can't pickle _csv.reader objects. Wydaje się również, że mapPartitionsautomatycznie wywołuje jakiś odpowiednik "readlines" na csv.readerobiekcie, gdzie z map, musiałem wywołać __next__jawnie, aby uzyskać listy z csv.reader. 2) Gdzie flatMapwchodzi? Samo dzwonienie mapPartitionszadziałało dla mnie.
Galen Long
1
rdd.mapPartitions(lambda x: csv.reader(x))działa, ponieważ mapPartitionsoczekuje Iterableobiektu. Jeśli chcesz być wyraźny, możesz użyć wyrażenia ze zrozumieniem lub generatora. mapsam nie działa, ponieważ nie wykonuje iteracji po obiekcie. Stąd moja propozycja użycia, flatMap(lambda x: csv.reader([x]))która będzie iterować po czytelniku. Ale tutaj mapPartitionsjest znacznie lepiej.
zero323
1
zauważ, że to odczyta nagłówek jako wiersz danych, a nie jako nagłówek
mion
7

To jest w PYSPARKU

path="Your file path with file name"

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

Następnie możesz sprawdzić

df.show(5)
df.count()
amarnath pryszcz
źródło
6

Jeśli chcesz załadować csv jako ramkę danych, możesz wykonać następujące czynności:

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

U mnie to działało dobrze.

Jeril
źródło
@GalenLong, jeśli nie masz nic przeciwko, czy możesz podzielić się już istniejącą odpowiedzią
Jeril
Dziwne, przysięgam, że w przypadku tego rozwiązania była inna odpowiedź. Może pomyliłem to z innym pytaniem. Mój błąd.
Galen Long
5

Jest to zgodne z tym, co początkowo sugerował JP Mercier na temat używania Pand, ale z dużą modyfikacją: jeśli wczytujesz dane do Pand w kawałkach, powinno to być bardziej plastyczne. Oznacza to, że możesz przeanalizować znacznie większy plik, niż Pandy może obsłużyć jako pojedynczy element i przekazać go do Spark w mniejszych rozmiarach. (To również odpowiada na komentarz dotyczący tego, dlaczego ktoś chciałby używać Sparka, skoro i tak mogą załadować wszystko do Pand).

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()
abby sobh
źródło
5

Teraz jest też inna opcja dla dowolnego ogólnego pliku csv: https://github.com/seahboonsiew/pyspark-csv w następujący sposób:

Załóżmy, że mamy następujący kontekst

sc = SparkContext
sqlCtx = SQLContext or HiveContext

Najpierw roześlij pyspark-csv.py do programów wykonawczych za pomocą SparkContext

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

Odczytaj dane CSV za pośrednictwem SparkContext i przekonwertuj je na DataFrame

plaintext_rdd = sc.textFile('hdfs://x.x.x.x/blah.csv')
dataframe = pycsv.csvToDataFrame(sqlCtx, plaintext_rdd)
optymista
źródło
3

Jeśli twoje dane csv nie zawierają znaków nowej linii w żadnym z pól, możesz załadować swoje dane textFile()i przeanalizować je

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)
iec2011007
źródło
2

Jeśli masz jeden lub więcej wierszy z mniejszą lub większą liczbą kolumn niż 2 w zbiorze danych, może wystąpić ten błąd.

Jestem też nowy w Pyspark i próbuję odczytać plik CSV. Poniższy kod zadziałał dla mnie:

W tym kodzie używam zestawu danych z kaggle, łącze to: https://www.kaggle.com/carrie1/ecommerce-data

1. Bez wspominania o schemacie:

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

Teraz sprawdź kolumny: sdfData.columns

Wynik będzie:

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

Sprawdź typ danych dla każdej kolumny:

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

To da ramkę danych ze wszystkimi kolumnami z typem danych jako StringType

2. Ze schematem: Jeśli znasz schemat lub chcesz zmienić typ danych dowolnej kolumny w powyższej tabeli, użyj tego (powiedzmy, że mam następujące kolumny i chcę, aby były w określonym typie danych dla każdej z nich)

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)

Teraz sprawdź schemat dla typu danych każdej kolumny:

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

Edytowano: możemy również użyć następującego wiersza kodu bez wyraźnego wspominania o schemacie:

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

Wynik to:

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

Wynik będzie wyglądał następująco:

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
Yogesh
źródło
1

Podczas korzystania spark.read.csvuważam, że korzystając z opcji escape='"'i multiLine=Truezapewniam najbardziej spójne rozwiązanie do standardu CSV , az mojego doświadczenia wynika, że najlepiej działa z plikami CSV wyeksportowanymi z Arkuszy Google.

To jest,

#set inferSchema=False to read everything as string
df = spark.read.csv("myData.csv", escape='"', multiLine=True,
     inferSchema=False, header=True)
flow2k
źródło
skąd pochodzi iskra? czy to import pyspark as sparkjest
Luk Aron
@LukAron W powłoce pyspark sparkjest już zainicjowany. W skrypcie przesłanym przez spark-submitmożesz utworzyć jego wystąpienie jako from pyspark.sql import SparkSession; spark = SparkSession.builder.getOrCreate().
flow2k