我有一个Spark 2.0.2群集,我通过Puppark通过Jupyter Notebook进行攻击。我有多个管道分隔的txt文件(加载到HDFS中,但在本地目录中也可用),我需要使用spark-csv将其加载到三个独立的数据帧中,具体取决于文件的名称。
我看到了我可以采取的三种方法 - 要么我可以使用python以某种方式遍历HDFS目录(尚未弄清楚如何执行此操作,加载每个文件然后进行联合。
我也知道在spark中存在一些通配符功能(参见here) - 我可以利用
最后,我可以使用pandas从磁盘加载vanilla csv文件作为pandas数据帧,然后创建一个spark数据帧。这里的缺点是这些文件很大,并且在单个节点上加载到内存中可能需要大约8GB。 (这就是为什么它首先转移到集群的原因。)
这是我到目前为止的代码和两个方法的一些伪代码:
import findspark
findspark.init()
import pyspark
from pyspark.sql import SparkSession
import pandas as pd
sc = pyspark.SparkContext(appName = 'claims_analysis', master='spark://someIP:7077')
spark = SparkSession(sc)
#METHOD 1 - iterate over HDFS directory
for currFile in os.listdir(HDFS:///someDir//):
if #filename contains 'claim':
#create or unionAll to merge claim_df
if #filename contains 'pharm':
#create or unionAll to merge pharm_df
if #filename contains 'service':
#create or unionAll to merge service_df
#Method 2 - some kind of wildcard functionality
claim_df = spark.read.format('com.databricks.spark.csv').options(delimiter = '|',header ='true',nullValue ='null').load('HDFS:///someDir//*<claim>.csv')
pharm_df = spark.read.format('com.databricks.spark.csv').options(delimiter = '|',header ='true',nullValue ='null').load('HDFS:///someDir//*<pharm>.csv')
service_df = spark.read.format('com.databricks.spark.csv').options(delimiter = '|',header ='true',nullValue ='null').load('HDFS:///someDir//*<service>.csv')
#METHOD 3 - load to a pandas df and then convert to spark df
for currFile in os.listdir(HDFS:///someDir//)
pd_df = pd.read_csv(currFile, sep = '|')
df = spark.createDataFrame(pd_df)
if #filename contains 'claim':
#create or unionAll to merge claim_df
if #filename contains 'pharm':
#create or unionAll to merge pharm_df
if #filename contains 'service':
#create or unionAll to merge service_df
有谁知道如何实施方法1或2?我还没有能够解决这些问题。此外,我感到惊讶的是,没有更好的方法将csv文件加载到pyspark数据帧中 - 使用第三方软件包看起来应该是本机功能让我感到困惑(我是否只是错过标准使用将csv文件加载到数据帧中的情况?)最后,我将把一个统一的单个数据帧写回HDFS(使用.write.parquet()),这样我就可以清除内存并使用一些分析MLlib。如果我强调的方法不是最佳实践,我会很感激推动正确的方向!
答案 0 :(得分:8)
方法1:
在python中你不能直接引用HDFS位置。你需要得到像pydoop这样的另一个图书馆的帮助。在scala和java中,你有API。即使使用pydoop,您也将逐个阅读文件。一个接一个地读取文件并且不使用spark提供的并行读取选项是不好的。
方法2:
您应该可以使用逗号分隔或使用通配符指向多个文件。这种方式spark负责读取文件并将它们分配到分区中。但是,如果你对每个数据框使用union选项,那么当你动态读取每个文件时会有一个边缘情况。当您拥有大量文件时,列表可能会在驱动程序级别变得如此巨大并且可能导致内存问题。主要原因是,读取过程仍然发生在驱动程序级别。
此选项更好。 spark将读取与regex相关的所有文件并将其转换为分区。你得到一个RDD用于所有的通配符匹配,从那里你不需要担心个别rdd的联合
示例代码cnippet:
server: Cowboy
connection: close
x-powered-by: Express
access-control-allow-origin: *
content-type: application/json; charset=utf-8
content-length: 151
etag: W/"97-PwoFrPd7F7BFa7ZI257AdQ"
date: Wed, 14 Dec 2016 02:58:26 GMT
via: 1.1 vegur
方法3:
除非你在python中有一些使用pandas功能的遗留应用程序,否则我更愿意使用spark提供的API
答案 1 :(得分:0)
我登陆这里试图完成类似的事情。我有一个函数可以读取HDFS并返回列表字典。
def get_hdfs_input_files(hdfs_input_dir):
"""Returns a dictionary object with a file list from HDFS
:rtype: dict
"""
import subprocess
sub_proc_cmd = "hdfs dfs -ls " + hdfs_input_dir + " | awk '{print $8}'"
process = subprocess.run(sub_proc_cmd, shell=True, stdout=subprocess.PIPE)
decoded_process = process.stdout.decode('utf-8')
file_list = decoded_process.split("\n")
claim_list, pharma_list, service_list = [], [], []
for file in file_list:
if file[-4:] == 'claim':
claim_list.append(file)
elif file[-4:] == 'pharma':
pharma_list.append(file)
elif file[-3:] == 'service':
service_list.append(file)
ret_dict = {'claim': claim_list, 'pharma': pharma_list, 'service': service_list}
return ret_dict
一旦有了CSV文件列表,就可以使用Pyspark将它们全部读入RDD。 docs声明CSV DataFrameReader将接受一个“字符串或字符串列表,作为输入路径或存储CSV行的字符串的RDD”。只需向该方法传递文件列表即可。
file_list = get_hdfs_input_files('/some/hdfs/dir')
claim_df = spark.read.csv(my_list.get('claim'),
delimiter = '|',header ='true',nullValue ='null')
pharma_df = spark.read.csv(my_list.get('pharma'),
delimiter = '|',header ='true',nullValue ='null')
service_df = spark.read.csv(my_list.get('service'),
delimiter = '|',header ='true',nullValue ='null')