因为机器学习等算法的快速发展,python已经成为重要的开发语言。利用python做数据挖掘时,Pandas、numpy是常见的依赖库,Pandas、nump在本质上是将数据一次性读入内存后再处理。因pc资源,尤其是内存等资源的限制,当数据量变大后,再用上述方法时,可能发生内存不足,或者剩余内存很少,导致无法处理,或者处理速度很慢的情况。hadoop和spark是主流的大数据平台,主流语言是Java和Scala,为应对python的快速发展,spark推出了python版:pyspark。pyspark可以在单机上使用,解决大数据问题。
本文主要从pyspark原理、pyspark常见操作两个角度进行讲解。
关键词:shuffle
在Pandas、numpy进行数据处理时,一次性将数据读入内存中,当数据很大时内存溢出,无法处理;同时而且很多执行算法是单线程处理,不能充分利用cpu性能。
spark在中最关键的概念之一是shuffle,他将数据集分成数据块:shuffle。在读取数据时,每个进程(线程)读取一个数据块,轮片将所有的数据处理完,解决了数据量巨大问题,极大的利用了CPU资源。同时,支持分布式结构,弹性拓展硬件资源。
小结:
1:不是将数据一次性全部读入内存中,而是分片,用时间换空间。
2:支持分布式,弹性拓展硬件资源。
安装方式:pip install pyspark
在数据结构上Spark支持dataframe、sql和rdd模型。
Dataframe与pandas很像
在pandas中,我们所有的动作都是写完理解执行。spark引入了两个概念:算子和转换。
算子和转换是Spark中最重要的两个动作。算子好比是盖房子中的画图纸,转换是搬砖盖房子。有时候我们做一个统计是多个动作结合的组合拳,spark常将一系列的组合写成算子的组合执行,执行时,spark会对算子进行简化等优化动作,执行速度更快。
关键词:
开始spark的快速入门,强烈多看官方原始文档:
Getting Started - Spark 2.4.1 Documentationspark.apache.org Welcome to Spark Python API Docs!spark.apache.org头文件
import
Spark使用时需要对每次都对脚本进行配置,也就是spark代码中进行设置。
spark=SparkSession.builder.config("spark.default.parallelism", 3000).appName("taSpark").getOrCreate()
解释:
A: config("spark.default.parallelism", 3000)
对数据进行切片,专业名词叫shuffle。因为我读取的数据是20g,所以设置成3000份,每次每个进程(线程)读取一个shuffle,避免内存不足的异常。
B: appName("taSpark")
设置程序的名字
参考链接:
pyspark.sql modulespark.apache.org方法一:
data = spark.read.csv(cc,header=None, inferSchema="true")
方法二
data = spark.read.format(csv).load(cc, header=None, inferSchema="true")
解释:
方法一:
该方法是只读csv格式的文件,其中cc是文件的路径,spark支持list格式,既cc是一个list,每个元素是一个csv文件的路径。
header=None,同pandas,指定表头
inferSchema="true",csv中的数据格式在读取时自动推断。注意,与pandas不同,这个必须要指定,且每个列只是一种数据类型。
查看读取数据的数据类型:data.printSchema()
|-- 用户手机号: long (nullable = true)
|-- 开始时间: integer (nullable = true)
|-- TAI: string (nullable = true)
|-- 次数: integer (nullable = true)
|-- 持续时间: integer (nullable = true)
|-- 流量: long (nullable = true)
|-- 日期: long (nullable = true)
|-- 小时: integer (nullable = true)
修改dataframe的表头,重定义dataframe
data=data.toDF(u"用户手机号",u"开始时间",u"TAI",u"次数",u"持续时间",u"流量")
3、 操作
Spark与Pandas中DataFrame对比(详细)
Spark与Pandas中DataFrame对比(详细) - 宁哥的小站www.lining0806.coma: 添加一列数据
方法一:
# 添加id
data=data.withColumn("id", monotonically_increasing_id())
b=data.select(data.id,data["开始时间"].astype("string")).rdd.map(lambda x:[x[0],int(x[1][-4:-2])]).
toDF("id:long,day: int")
# 插入列
data=data.join(b,data.id==b.id)
解释:
因为spark是在每个shuffle中,不能直接插入,要进行类似合表的动作。
思路:
添加index,然后在以index为key,对数据进行合并。
data=data.withColumn("id", monotonically_increasin)
withColumn函数是添加一列数据,但是这个数据必须是原始表中的数据。"id"是新添加列的名称,monotonically_increasin是给每个行一个key,到达添加index的效果。
data.select(data.id,data["开始时间"].astype("string")).rdd.map(lambda x:[x[0],int(x[1][-4:-2])]).toDF("id:long,day: int")
select(data.id,data["开始时间"].astype("string")),从数据中读取id列和“开始时间”列,同时把开始“开始时间”转换成string数据类型。此时的数据类型是dataframe,把他们转成rdd然后操作。Rdd的map是对每一行,每行的数据类型是Row,最后要转换城dataframe要转换成list或者row,本次转换,我们将其转换成list。
toDF("id:long,day: int")
将RDD构建成dataframe,指定列名和数据类型。注意,数据类型要与原始的一致。
缺点:因为join,该方法时间较久。
方法二
使用rdd的map方法,该方法速度快,但是不适合其他表的数据插入。
# 加入一个表名
# 保留原始的名字
StructType = data.schema
# 添加一个字段
StructType.add(StructField("日期", LongType()))
StructType.add(StructField("小时", IntegerType()))
def f(x):
xtmp=list(x.asDict().values())
c=int(str(x["开始时间"])[-4:-2])
xtmp.append(c)
c=int(str(x["开始时间"])[-2:])
xtmp.append(c)
return xtmp
pass
datardd=data.rdd.map(f)
data=spark.createDataFrame(datardd,StructType)
b: 修改一列数据:
# 日期替换成星期
placedict = {24: 7, 25: 1, 26: 2, 27: 3, 28: 4, 29: 5, 30: 6}
def f(x):
x=x.asDict()
try:
x['日期']=placedict[x['日期']]
pass
except:
x['日期']=-1
pass
return list(x.values())
pass
# 保存原来的头
StructType = data.schema
# 转换
datardd = data.rdd.map(f)
# 转换完成
data = spark.createDataFrame(datardd, StructType)
c: 统计
1. c=data.groupby(["小时","日期","用户手机号"]).count().groupBy("小时","日期").count().toPandas()
2. c=c.sort_values(by=["日期","小时"])
3. a=data.groupBy("小时","日期").agg({"流量":"sum"})
4. e=e.withColumn("avg(sum(流量))", e["avg(sum(流量))"]/(1024*1024)).rderBy(["日期", "小时"], ascending=[1, 1]).toPandas()
5. data.foreach(lambda x: int(str(x["开始时间"])[-4:-2]))
groupby与pandas一致
sort_values降序排列,ascending=[1, 1],设置某列是升序还是降序
agg({"流量":"sum"}),因为shuffle比较多,每次历遍都很繁琐,所以可以对特定列做特定的动作。
data.foreach(lambda x: int(str(x["开始时间"])[-4:-2])),与pandas的apply一样。
持久化
d: 做一次持久化
算子每次都要从头开始执行,有时候很多动作是重发的,也就是起始点是一样的,因此可以做一个持久化,也就是把动作提前做完,保存好生成的数据。包括cache和presist,其中cache是把数据保存在内存,不推荐。Persist与read类似,推荐。
e: 写入
data.repartition(1).write.csv("test/ok",mode="overwrite")
写入是写入一个文件中,其中repartition(1)是告诉写入的文件个数,默认是与shuffle个数相同,可以不用这个参数。
4、 SQL模式
# sql
#创造表名
data.createOrReplaceTempView("plan_AllData")
# 获取最大最小值
longitudeMAX_MIN=spark.sql("SELECT MAX(longitude),MIN(longitude) FROM plan_AllData").collect()
latitudeMAX_MIN=spark.sql("SELECT MAX(latitude),MIN(latitude) FROM plan_AllData").collect()
与使用sql相同,返回的是dataframe结构的数据