Tải tệp CSV bằng Spark


110

Tôi mới sử dụng Spark và tôi đang cố đọc dữ liệu CSV từ một tệp với Spark. Đây là những gì tôi đang làm:

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

Tôi mong đợi cuộc gọi này cung cấp cho tôi danh sách hai cột đầu tiên trong tệp của tôi nhưng tôi gặp lỗi này:

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

mặc dù tệp CSV của tôi có nhiều hơn một cột.

Câu trả lời:


63

Bạn có chắc chắn rằng tất cả các dòng có ít nhất 2 cột? Bạn có thể thử một cái gì đó như, chỉ để kiểm tra ?:

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

Ngoài ra, bạn có thể in thủ phạm (nếu có):

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

Đó là nó, một dòng chỉ với một cột, cảm ơn bạn.
Kernael

2
Tốt hơn nên phân tích cú pháp bằng cách sử dụng csvthư viện tích hợp để xử lý tất cả việc thoát vì chỉ cần tách bằng dấu phẩy sẽ không hoạt động nếu, chẳng hạn, có dấu phẩy trong các giá trị.
sudo

4
Có rất nhiều công cụ để phân tích cú pháp csv, đừng phát minh lại bánh xe
Stephen

2
Mã này sẽ bị hỏng nếu có dấu phẩy bên trong dấu ngoặc kép. Phân tích cú pháp csv phức tạp hơn là chỉ tách tại ",".
Alceu Costa

Điều này ngắt cho dấu phẩy. Thật tồi tệ.
rjurney

184

Spark 2.0.0+

Bạn có thể sử dụng trực tiếp nguồn dữ liệu csv tích hợp sẵn:

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

hoặc là

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

mà không bao gồm bất kỳ phụ thuộc bên ngoài nào.

Spark <2.0.0 :

Thay vì phân tích cú pháp thủ công, điều này không quá tầm thường trong trường hợp chung, tôi khuyên bạn nên spark-csv:

Hãy chắc chắn rằng Spark CSV được bao gồm trong các đường dẫn ( --packages, --jars, --driver-class-path)

Và tải dữ liệu của bạn như sau:

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

Nó có thể xử lý tải, suy luận lược đồ, bỏ các dòng không đúng định dạng và không yêu cầu chuyển dữ liệu từ Python sang JVM.

Ghi chú :

Nếu bạn biết lược đồ, tốt hơn là bạn nên tránh suy luận lược đồ và chuyển nó cho DataFrameReader. Giả sử bạn có ba cột - số nguyên, đôi và chuỗi:

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
Nếu bạn làm điều này, đừng quên bao gồm gói csv databricks khi bạn mở pyspark shell hoặc sử dụng spark-submit. Ví dụ: pyspark --packages com.databricks:spark-csv_2.11:1.4.0(đảm bảo thay đổi cơ sở dữ liệu / phiên bản tia lửa thành những phiên bản bạn đã cài đặt).
Galen Long

Nó là csvContext hay sqlContext trong pyspark? Bởi vì trong scala, bạn cần 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())

sử dụng dấu phân tách 'sep không phải' như sau: df = spark.read.csv ("/ home / stp / test1.csv", header = True, sep = "|")
Grant Shannon

18

Và một tùy chọn khác bao gồm đọc tệp CSV bằng Pandas và sau đó nhập Pandas DataFrame vào Spark.

Ví dụ:

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
Tại sao OP muốn làm trên tia lửa nếu ông có khả năng tải dữ liệu trong gấu trúc
WoodChopper

Không muốn cài đặt hoặc chỉ định phụ thuộc trên mọi cụm tia lửa ....
SummerEla

Panda cho phép phân tách tệp khi đọc vì vậy vẫn có trường hợp sử dụng ở đây là để Pandas xử lý phân tích cú pháp tệp ban đầu. Xem câu trả lời của tôi bên dưới để biết mã.
abby sobh

Thận trọng: Gấu trúc cũng xử lý lược đồ cột theo cách khác với spark đặc biệt là khi có các khoảng trống liên quan. An toàn hơn khi chỉ tải csv dưới dạng chuỗi cho mỗi cột.
AntiPawn79

@WoodChopper Bạn có thể sử dụng Pandas làm UDF trong Spark, phải không?
flow2k

16

Chỉ cần tách bằng dấu phẩy cũng sẽ chia các dấu phẩy nằm trong các trường (ví dụ a,b,"1,2,3",c), vì vậy không nên. Câu trả lời của zero323 là tốt nếu bạn muốn sử dụng API DataFrames, nhưng nếu bạn muốn sử dụng Spark cơ sở, bạn có thể phân tích cú pháp csv trong Python cơ sở với mô-đun csv :

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

CHỈNH SỬA: Như @muon đã đề cập trong các nhận xét, điều này sẽ coi tiêu đề giống như bất kỳ hàng nào khác, vì vậy bạn sẽ cần phải giải nén nó theo cách thủ công. Ví dụ: header = rdd.first(); rdd = rdd.filter(lambda x: x != header)(đảm bảo không sửa đổi headertrước khi bộ lọc đánh giá). Nhưng tại thời điểm này, có lẽ bạn nên sử dụng trình phân tích cú pháp csv tích hợp sẵn.


1
Bạn không cần Hive để sử dụng DataFrames. Về giải pháp của bạn: a) Không cần thiết StringIO. csvcó thể sử dụng bất kỳ có thể lặp lại nào b) __next__không nên được sử dụng trực tiếp và sẽ không thành công trên dòng trống. Hãy nhìn vào flatMap c) Nó sẽ là hiệu quả hơn để sử dụng mapPartitionsthay vì khởi đầu đọc trên mỗi dòng :)
zero323

Cảm ơn rất nhiều cho các sửa chữa! Trước khi chỉnh sửa câu trả lời của mình, tôi muốn đảm bảo rằng mình đã hiểu đầy đủ. 1) Tại sao nó rdd.mapPartitions(lambda x: csv.reader(x))hoạt động trong khi rdd.map(lambda x: csv.reader(x))ném một lỗi? Tôi mong đợi cả hai sẽ ném như nhau TypeError: can't pickle _csv.reader objects. Nó cũng có vẻ như mapPartitionstự động gọi một số tương đương với "đường đọc" trên csv.readerđối tượng, trong đó map, tôi cần gọi __next__một cách rõ ràng để đưa danh sách ra khỏi csv.reader. 2) Đi flatMapvào đâu? Chỉ gọi mapPartitionsmột mình đã làm việc cho tôi.
Galen Long

1
rdd.mapPartitions(lambda x: csv.reader(x))hoạt động vì mapPartitionsmong đợi một Iterableđối tượng. Nếu bạn muốn rõ ràng, bạn có thể hiểu hoặc biểu thức trình tạo. mapmột mình không hoạt động vì nó không lặp qua đối tượng. Do đó, đề xuất của tôi để sử dụng flatMap(lambda x: csv.reader([x]))sẽ lặp lại trên người đọc. Nhưng mapPartitionsở đây tốt hơn nhiều.
0323

1
lưu ý rằng điều này sẽ đọc tiêu đề dưới dạng một hàng dữ liệu, không phải dưới dạng tiêu đề
muon

7

Đây là ở PYSPARK

path="Your file path with file name"

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

Sau đó, bạn có thể kiểm tra

df.show(5)
df.count()

6

Nếu bạn muốn tải csv dưới dạng khung dữ liệu thì bạn có thể làm như sau:

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

Nó làm việc tốt cho tôi.


@GalenLong nếu bạn không phiền, bạn có thể chia sẻ câu trả lời đã có
Jeril

Kỳ lạ, tôi thề rằng có một câu trả lời khác với giải pháp này. Có lẽ tôi đã nhầm lẫn điều này với một câu hỏi khác. Lỗi của tôi.
Galen Long

5

Điều này phù hợp với những gì JP Mercier đề xuất ban đầu về việc sử dụng Gấu trúc, nhưng với một sửa đổi lớn: Nếu bạn đọc dữ liệu thành Gấu trúc theo từng đoạn, nó sẽ dễ uốn hơn. Có nghĩa là bạn có thể phân tích cú pháp một tệp lớn hơn nhiều so với tệp Pandas thực sự có thể xử lý như một phần duy nhất và chuyển nó đến Spark với kích thước nhỏ hơn. (Điều này cũng trả lời nhận xét về lý do tại sao một người muốn sử dụng Spark nếu họ có thể tải mọi thứ vào 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

Giờ đây, cũng có một tùy chọn khác cho bất kỳ tệp csv chung nào: https://github.com/seahboonsiew/pyspark-csv như sau:

Giả sử chúng ta có bối cảnh sau

sc = SparkContext
sqlCtx = SQLContext or HiveContext

Đầu tiên, hãy phân phối pyspark-csv.py cho những người thực thi bằng SparkContext

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

Đọc dữ liệu csv qua SparkContext và chuyển đổi nó thành DataFrame

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

3

Nếu dữ liệu csv của bạn không chứa dòng mới trong bất kỳ trường nào, bạn có thể tải dữ liệu của mình textFile()và phân tích cú pháp

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

Nếu bạn đang có bất kỳ một hoặc nhiều hàng có số cột ít hơn hoặc nhiều hơn 2 trong tập dữ liệu thì lỗi này có thể phát sinh.

Tôi cũng mới sử dụng Pyspark và đang cố đọc tệp CSV. Mã sau phù hợp với tôi:

Trong đoạn mã này, tôi đang sử dụng tập dữ liệu từ kaggle, liên kết là: https://www.kaggle.com/carrie1/ecommerce-data

1. Không đề cập đến lược đồ:

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

Bây giờ hãy kiểm tra các cột: sdfData.columns

Đầu ra sẽ là:

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

Kiểm tra loại dữ liệu cho mỗi cột:

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

Điều này sẽ cung cấp cho khung dữ liệu với tất cả các cột có kiểu dữ liệu là StringType

2. Với giản đồ: Nếu bạn biết lược đồ hoặc muốn thay đổi kiểu dữ liệu của bất kỳ cột nào trong bảng trên thì hãy sử dụng điều này (giả sử tôi đang có các cột sau và muốn chúng ở một kiểu dữ liệu cụ thể cho từng cột đó)

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)

Bây giờ hãy kiểm tra lược đồ cho loại dữ liệu của mỗi cột:

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

Đã chỉnh sửa: Chúng tôi cũng có thể sử dụng dòng mã sau mà không cần đề cập đến lược đồ một cách rõ ràng:

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

Đầu ra là:

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

Đầu ra sẽ như thế này:

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

Khi sử dụng spark.read.csv, tôi thấy rằng việc sử dụng các tùy chọn escape='"'multiLine=Truecung cấp giải pháp nhất quán nhất cho tiêu chuẩn CSV và theo kinh nghiệm của tôi, hoạt động tốt nhất với các tệp CSV được xuất từ ​​Google Trang tính.

Đó là,

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

tia lửa đến từ đâu? là nó import pyspark as spark?
Luk Aron

@LukAron Trong một trình bao pyspark, sparkđã được khởi tạo. Trong một tập lệnh được gửi bởi spark-submit, bạn có thể tạo nó bằng from pyspark.sql import SparkSession; spark = SparkSession.builder.getOrCreate().
flow2k
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.