pyspark dataframe实现行循环,调用Python 实现大批量小文件处理,对大批量用户实现用户画像
适合应用场景
集群处理大批量的小文件,如需要对1000万用户构建用户画像,每个用户的数据不大有几百M(单机Python能处理的程度),可以这么调用,而且之前写的单Python代码直接复制粘贴稍微改动就可以直接用
直接上代码
# -*- coding: utf-8 -*-
# @Time : 2019/6/6 10:40
# @Author : WP
from pyspark.sql.session import SparkSession
import os
import pandas as pd
import numpy as np
# spark初始化
spark = SparkSession\
.builder \
.appName("Dataframe_circ")\
.getOrCreate()
sc = spark.sparkContext
# 不显示日志,只显示Java报错日志
sc.setLogLevel('ERROR')
"""按vid进行分组"""
def build(r):
"""
:param r: spark dataframe 行
:return: 键值对
"""
r = r.asDict() if type(r) is not dict else r
resultkey = r["VID"]
# spark dataframe按行存为字典
resultvalue = {}
for key in r.keys():
resultvalue[key] = r[key]
return (resultkey, resultvalue)
"""根据key对数据进行合并"""
def merge(x, y):
"""
:param x: 键值对
:param y: 键值对
:return: vid为key的列表
"""
# 在每个节点执行完后执行
if isinstance(x, list) and isinstance(y, list):
x.extend(y)
return x
elif isinstance(x, list):
x.append(y)
return x
elif isinstance(y, list):
y.append(x)
return y
# 最先执行,最开始x,y都不为list
elif x and y:
return [x, y]
elif x:
return [x]
else:
return [y]
"""map分开处理每个VOD的数据,若添加Python 函数在此函数添加"""
def make_dataframe(data):
"""
:param data:以VID为关键字的列表
:return: 对应处理后的VID数据
"""
c_data = pd.DataFrame(data[1] if type(data[1]) == list else [data[1]])
data_need = c_data[c_data["size"] > 5]
data_need = data_need.reset_index(drop=True)
return p_spark(data_need)
"""python dataframe 转 spark rdd"""
def p_spark(data):
"""
:param data: 每个 vid 对应的python dataframe
:return: 每个 VID 的 list
"""
# 把每个VID 的 Python dataframe的所有行以字典形式存在list中
list_all = []
for i in range(len(data)):
# 提取Python dataframe的每一行存为字典格式
dict_need = dict(data.loc[i, :])
for key in dict_need.keys():
# 因spark dataframe的编码格式不支持np的数据格式需要对np的数据进行重新编码
if isinstance(dict_need[key], (np.float64, np.float32, np.float)):
dict_need[key] = float(dict_need[key])
elif isinstance(dict_need[key], (np.int64, np.int, np.int32)):
dict_need[key] = int(dict_need[key])
list_all.append(dict_need)
return list_all
if __name__ == '__main__':
data_l = [[1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3],
[5, 8, 10, 4, 6, 10, 20, 5, 9, 2, 2]]
data_l = [{"VID": data_l[0][i], "size": data_l[1][i]} for i in range(len(data_l[0]))]
# 创建pyspark dataframe
data_d = spark.createDataFrame(data_l)
data_d.show()
# 创建每个id对应的列表
data_rdd = data_d.rdd.map(lambda r: build(r)).reduceByKey(merge)
print("pyspark dataframe 转为rdd")
# 集群数据记得删除collect,内存会爆de
print(data_rdd.collect())
# 对每个id对应的数据通过Python 的dataframe的函数进行处理
data_t = data_rdd.flatMap(lambda k: make_dataframe(k))
print("经过单节点的Python处理过的数据")
print(data_t.collect())
data_all = spark.createDataFrame(data_t)
data_all.show()
说明
data_d = spark.createDataFrame(data_l)
这里为创建的spark dataframe 样例,模拟大批量需要处理的小文件,两列一列vid,一列size,集群数据读取直接读取为dataframe
这里为data_rdd 通过data_d转换而来,一个大列表,内容为键值对,每个键值对的id 为 vid,内容为vid 对应的所有数据,为列表形式可以通过map直接用pandas 的 dataframe进行处理
[(1, [{‘VID’: 1, ‘size’: 5}, {‘VID’: 1, ‘size’: 8}, {‘VID’: 1, ‘size’: 10}]), (2, [{‘VID’: 2, ‘size’: 4}, {‘VID’: 2, ‘size’: 6}, {‘VID’: 2, ‘size’: 10}, {‘VID’: 2, ‘size’: 20}]), (3, [{‘VID’: 3, ‘size’: 9}, {‘VID’: 3, ‘size’: 2}, {‘VID’: 3, ‘size’: 2}, {‘VID’: 3, ‘size’: 5}])]
make_dataframe函数里面就是调用 Python 的 pandas dataframe 对数据进行处理,这里我仅仅是对每个vid进行了筛选size>5的行,在进行处理的时候大家只需要把以前的单机Python代码复制粘贴过来即可,这是在处理过程中打印出来的pd 的 dataframe,spark的dataframe的操作不如Python的dataframe方便,而且大家基本都是先学的Python 再接触的spark,对spark的了解不如Python深刻,这样就可以很好的把Python和spark进行结合。这样Python的机器学习的包,深度学习的包也都可以调用了。
函数 p_spark主要实现把python dataframe处理完的数据存为列表形式 再变为rdd
data_t的输出数据
[{‘VID’: 1, ‘size’: 8}, {‘VID’: 1, ‘size’: 10}, {‘VID’: 2, ‘size’: 6}, {‘VID’: 2, ‘size’: 10}, {‘VID’: 2, ‘size’: 20}, {‘VID’: 3, ‘size’: 9}]
把data_t的数据再创建为spark的dataframe 即可再进行数据写出了,按照vid进行分包,或者什么其他格式都可以了。
这是处理完成过后的数据的saprk dataframe
过程到此结束,作者水平有限,大家发现什么问题也请大家留言。