Spark代码案例

一、SparkSQL案例(电影数据统计)

数据源:http://files.grouplens.org/datasets/movielens/ml-100k/u.data

复制网页中数据到本地的文件中

ctrl+a  全部选中

ctrl+c  复制

ctrl+v  粘贴

ctrl+s  保存

将本地的数据文件上传的hdfs

字段: 用户id  电影id 评分 时间

需求:

  • 查询每个用户平均分
  • 查询每个电影平均分
  • 查询大于平均分的电影的数量
  • 查询高分电影中(>3)打分次数最多的用户, 并求出此人打的平均分
  • 查询每个用户的平均打分, 最低打分, 最高打分
  • 查询被评分超过100次的电影, 的平均分 排名 TOP10
from pyspark.sql import SparkSession, functions as F
from pyspark.sql.types import *

ss = SparkSession.builder.getOrCreate()

sc = ss.sparkContext

rdd = sc.textFile('/movie')

# 对每行数据进行切割转为二维嵌套列表
rdd_map = rdd.map(lambda x: [x.split('\t')[0], x.split('\t')[1], int(x.split('\t')[2]), x.split('\t')[3]])

# 将rdd转为df
schema_type = StructType(). \
    add('user_id', StringType()). \
    add('movie_id', StringType()). \
    add('score', IntegerType()). \
    add('date_time', StringType())

df = rdd_map.toDF(schema_type)

df.show()

# 查询每个用户平均分
df_avg_user = df.groupby('user_id').agg(F.avg('score').alias('avg_data'))
df_avg_user.show()

# 查询每个电影平均分
df_avg_movie = df.groupby('movie_id').agg(F.avg('score').alias('avg_data'))
df_avg_movie.show(30, truncate=False)

# 查询大于平均分的电影的数量

# 1- 求整体平均分
df_avg_data = df.select(F.avg('score'))
df_avg_data.show()
# 取出df中的第一行数据 就是一个row对象
data = df_avg_data.first()[0]

# 2、通过每个电影的平均分大于总的平均分进行数据的过滤,统计电影数量
res = df_avg_movie.where(df_avg_movie['avg_data'] > data).count()
print(res)

# * 查询高分电影中(>3)打分次数最多的用户, 并求出此人打的平均分
# 1、找到大高分次数最多的用户
# df.where('score > 3').select(['user_id','movie_id','score']).orderBy('user_id').show()
user_r = df.where('score > 3').groupby('user_id').agg(F.count('user_id').alias('user_cnt')).orderBy('user_cnt',
                                                                                    ascending=False).first()
user_id = user_r['user_id']
print(user_id)

# 2、求出改用所有打分的平均分
df_user_avg = df.where(df['user_id'] == user_id).select(F.avg('score'))
df_user_avg.show()

# * 查询每个用户的平均打分, 最低打分, 最高打分
df_user = df_avg_user = df.groupby('user_id').agg(F.avg('score').alias('avg_data'),F.max('score').alias('max_data'),F.min('score').alias('min_data'))
df_user.show()

# * 查询被评分超过100次的电影, 的平均分 排名 TOP10
df_data = df.groupby('movie_id').agg(F.count('movie_id').alias('movie_cnt'),F.round(F.avg('score'),2).alias('moive_avg')).where('movie_cnt > 100').orderBy('moive_avg',ascending=False).limit(10)
df_data.show()

二、分区数目

在SparkSQL中当Job中产生Shuffle时,默认的分区数(spark.sql.shuffle.partitions )为200,在实际项目中要合理的设置

# spark计算配置
# config()
ss = SparkSession.builder.config('spark.sql.shuffle.partitions','4').getOrCreate()

三、数据清洗

  • 重复数据
    • 去重  dropDuplicates
# 使用sparksql都要SparkSession
from pyspark.sql import SparkSession
from pyspark.sql.types import *

# 创建SparkSession对象
ss = SparkSession.builder.getOrCreate()

sc = ss.sparkContext

rdd = sc.parallelize([
    [1,'张三',20,None],
    [2,'李四',22,'男'],
    [3, '王五', None, '男'],
    [4, '赵六', 22, '男'],
    [2, '李四', 22, '男']
])

schema_type = StructType().\
    add('id',IntegerType()).\
    add('name',StringType()).\
    add('age',IntegerType()).\
    add('gender',StringType())
df= rdd.toDF(schema_type)

# 去重
df.show()
# 去重后返回一个新的df
# 判断整行数据是否相同,整行数据相同,就去除
df_new = df.dropDuplicates()
df_new.show()

# 判断指定的字段数据是否重复,如果重复则删除整行数据
df_new2 = df.dropDuplicates(['id','age'])
df_new2.show()
  • 空数据
    • 去除空值
    • 空值替换
    • 建议空数据进行替换,避免删除一些有效数据  20字段  2个为空 省下18个字段是有效数据
      • 替换建议是随机值  sql方式  case when
# 使用sparksql都要SparkSession
from pyspark.sql import SparkSession,functions as F
from pyspark.sql.types import *

# 创建SparkSession对象
ss = SparkSession.builder.getOrCreate()

sc = ss.sparkContext

rdd = sc.parallelize([
    [1,'张三',20,None],
    [2,'李四',22,'男'],
    [3, '王五', None, None],
    [4, '赵六', 22, '男'],
    [2, '李四', 22, '男']
])

schema_type = StructType().\
    add('id',IntegerType()).\
    add('name',StringType()).\
    add('age',IntegerType()).\
    add('gender',StringType())
df= rdd.toDF(schema_type)

# 去空
df.show()
# 返回新的df
# 只要数据中有空值,就删除整行数据
df_new = df.dropna()
df_new.show()

# thresh 判断非空数据个数  thresh=2,当前行数据中至少有两个非空数据,如果低于两个,就删出整行数据
df_new1 = df.dropna(thresh=3)
df_new1.show()

# subset 指定判断非空数据的字段
df_new2 = df.dropna(thresh=3,subset=['name','age','gender'])
df_new2.show()

df_new2 = df.dropna(thresh=1,subset=['name'])
df_new2.show()

df.where('name is not null').show()

# 空值替换 返回一个新的df
df_new3 = df.fillna({'age':0,'gender':'0'})
df_new3.show()

四、数据源与格式

读取不同格式的文件数据转为dataframe

read方法进行读取

write方法将dataframe写入不同格式文件

4-1 数据获取

本地spark安装包中有测试文件可以读取

/export/server/spark/examples/src/main/resources

  • json {‘name’:‘张三’}
  • text  ‘1,张三’
  • csv  ‘1,张三’  可以设置表头
    • csv文件有两个部分构成  头部数据,也就是字段数据,行数数据
  • orc
  • parquet
  • mysql的连接操作
  • 实现etl,实现sqoop的操作  mysql数据导入数仓
  • 拷贝连接驱动包
    • cp /export/server/hive/lib/mysql-connector-java-5.1.32.jar /export/server/spark/jars/
from pyspark.sql import SparkSession,functions as f

ss = SparkSession.builder.getOrCreate()

#  读数据
df = ss.read.text('file:///export/server/spark/examples/src/main/resources/people.txt')
df.show()
df_new = df.select(f.split('value',',').alias('data'))
df_new.show()
df_new.select(df_new['data'][0],df_new['data'][1]).show()

# json 数据
df2 = ss.read.json('file:///export/server/spark/examples/src/main/resources/people.json')
df2.show()

# csv  header 是否保留头部数据  sep 指定分割
df3 = ss.read.csv('file:///export/server/spark/examples/src/main/resources/people.csv',sep=';',header=True)
df3.show()
# orc
df4 = ss.read.orc('file:///export/server/spark/examples/src/main/resources/users.orc')
df4.show()

df5 = ss.read.parquet('file:///export/server/spark/examples/src/main/resources/users.parquet')
df5.show()

# 读取mysql数据
# url= jdbc:mysql://node1:3306/itcast  连接mysql的信息  itcast是库名
# table='student' 指定表的名字
# properties 指定连接信息
df6 = ss.read.jdbc(url='jdbc:mysql://node1:3306/itcast?characterEncoding=UTF-8',table='student',properties={'user':'root','password':'123456','dirver':'com.mysql.jdbc.Driver'})
df6.show()

4-2 数据写入

因为数据是在df中存储,所以使用dataframe进行数据写入

写入文件有个模式,覆盖和追加两种方式

  • text
  • json
  • csv
  • orc
# 使用sparksql都要SparkSession
from pyspark.sql import SparkSession,functions as F
from pyspark.sql.types import *

# 创建SparkSession对象
ss = SparkSession.builder.getOrCreate()

sc = ss.sparkContext

rdd = sc.parallelize([
    [1,'张三'],
    [2,'李四']
])

schema_type = StructType().\
    add('id',IntegerType()).\
    add('name',StringType())

df= rdd.toDF(schema_type)

# 数据写入
# text 写入数据需要将字段数据拼接一个字符串形式一个字段写
# df.select(F.concat_ws(',','id','name')).write.text('/text_data')
# json
# 写入模式  mode
# df.write.json('/json_data',mode='append')
df.write.json('/json_data',mode='overwrite')
# csv
df.write.csv('/csv_data',mode='overwrite',sep=',',header=True)

df.write.orc('/orc_data',mode='overwrite')
df.write.parquet('/parquet_data',mode='overwrite')
  • mysql
# mysql数据写入
df.write.jdbc(url='jdbc:mysql://node1:3306/itcast?characterEncoding=UTF-8',mode='overwrite',table='student',properties={'user':'root','password':'123456','dirver':'com.mysql.jdbc.Driver'})

数据读取  sparksession

ss.read.jdbc(路径)

数据写入  dataframe

mode 指定写入模式  append 追加写入  overwrite 覆盖写

df.write.orc(路径,mode=写入模式)

五、窗口函数

over(partiton by 分组字段  order by 排序字段  rows 计算范围)

计算范围  between  。。。 and  。。。

between unbounded preceding  and  current row

between 1 preceding  and  current row

between current row  and  current row   一般不指定

between current row  and  1 following

between current row  and  unbounded  following

between 1 preceding  and  1 following

between unbounded preceding  and  unbounded  following

聚合函数(sum,avg,max,min,count)  over()

排序函数(rank,dense_rank,row_number,ntile) over()

取值函数(first_value,lead,lag,last_value) over()

窗口函数计算的结果会单独创建一列保存

# 导入window类 定义窗口
from pyspark.sql import SparkSession, Window, functions as F
from pyspark.sql.types import *

# 1、生成SparkSession对象
ss = SparkSession.builder.getOrCreate()
# 2、获取sparkcontext对象
sc = ss.sparkContext

# 3、 读取文件数据转为rdd
rdd = sc.textFile('/student')
# 4、查看rdd数据
# 5、对每行数据进行切割
rdd_map = rdd.map(
    lambda x: [int(x.split(',')[0]), x.split(',')[1], x.split(',')[2], int(x.split(',')[3]), x.split(',')[4]])

# 6、rdd转df
# 7、定义 表信息
schema_type = StructType(). \
    add('id', IntegerType()). \
    add('name', StringType()). \
    add('gender', StringType()). \
    add('age', IntegerType()). \
    add('cls', StringType())

df = rdd_map.toDF(schema_type)

df.show()

# 1、定义窗口
# partitionBy 指定分组字段  over(partition by 分组字段)
# orderBy 指定排序字段   over(order by 分组字段)
# rowsBetween 指定计算范围  over(row )
w = Window.partitionBy('gender').orderBy('id').rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing)
w1 = Window.partitionBy('gender').orderBy('age')


# 2、将窗口计算后的结果单独创建一列保存
# withColumn
# 参数一 指定增加的字段名称
# 参数二  指定计算方法
df_new = df.withColumn('window_data', F.sum('age').over(w))
df_new.show()

df_new1 = df.withColumn('window_data', F.rank().over(w1))
df_new1.show()

六、自定义函数

用户可以根据需求自己封装计算的逻辑,对字段数据进行计算

6-1 函数分类

  • udf 一进一出  传递一行数据返回一行计算的结果
  • udaf  多进一出   传递多行返回一行   聚合函数  借助pandas
  • udtf  一进多出  传递一行返回多行   爆炸函数

6-2 自定义函数使用–UDF

# 导入window类 定义窗口
from pyspark.sql import SparkSession, Window, functions as F
from pyspark.sql.types import *

# 1、生成SparkSession对象
ss = SparkSession.builder.getOrCreate()
# 2、获取sparkcontext对象
sc = ss.sparkContext

# 3、 读取文件数据转为rdd
rdd = sc.textFile('/student')
# 4、查看rdd数据
# 5、对每行数据进行切割
rdd_map = rdd.map(
    lambda x: [int(x.split(',')[0]), x.split(',')[1], x.split(',')[2], int(x.split(',')[3]), x.split(',')[4]])

# 6、rdd转df
# 7、定义 表信息
schema_type = StructType(). \
    add('id', IntegerType()). \
    add('name', StringType()). \
    add('gender', StringType()). \
    add('age', IntegerType()). \
    add('cls', StringType())

df = rdd_map.toDF(schema_type)

df.show()

# 自定义函数
def func(x,y):
    # x,y 接受传递字段的数据
    # 每次接受一行数据
    data = x+y
    return data

# 注册到spark中使用
# 第一个参数  指定注册的函数名
# 第二个参数  指定自己定义的函数名
# 第三个参数  指定返回结果的类型
# 注册完成后会返回注册的函数
concat_it = ss.udf.register('concat_it',func,StringType())

# DSL方法中使用
df_new = df.select(concat_it(df['name'],df['gender']))
df_new.show()

# SQL中使用
df.createTempView('stu')
df_new2 = ss.sql('select concat_it(name,gender) from stu ')
df_new2.show()
  • 装饰器注册
    • 只能在DSL方法中使用,在sql语句中无法使用
# 导入window类 定义窗口
from pyspark.sql import SparkSession, Window, functions as F
from pyspark.sql.types import *

# 1、生成SparkSession对象
ss = SparkSession.builder.getOrCreate()
# 2、获取sparkcontext对象
sc = ss.sparkContext

# 3、 读取文件数据转为rdd
rdd = sc.textFile('/student')
# 4、查看rdd数据
# 5、对每行数据进行切割
rdd_map = rdd.map(
    lambda x: [int(x.split(',')[0]), x.split(',')[1], x.split(',')[2], int(x.split(',')[3]), x.split(',')[4]])

# 6、rdd转df
# 7、定义 表信息
schema_type = StructType(). \
    add('id', IntegerType()). \
    add('name', StringType()). \
    add('gender', StringType()). \
    add('age', IntegerType()). \
    add('cls', StringType())

df = rdd_map.toDF(schema_type)

df.show()

# 自定义函数
@F.udf(returnType=StringType())
def func(x,y):
    # x,y 接受传递字段的数据
    # 每次接受一行数据
    data = x+y
    return data


# DSL方法中使用
df_new = df.select(func(df['name'],df['gender']))
df_new.show()
由于每个企业的具体实现细节和代码都有所不同,因此我无法提供所有企业案例代码。但是,我可以为您提供几个基本的Spark示例代码,以帮助您开始使用Spark: 1. 读取文本文件并计算单词数量: ```scala val textFile = spark.read.textFile("path/to/text/file") val wordCounts = textFile.flatMap(line => line.split(" ")).groupByKey(identity).count() wordCounts.show() ``` 2. 读取CSV文件并计算平均值: ```scala val csvFile = spark.read.format("csv").option("header", "true").load("path/to/csv/file") val average = csvFile.select(avg("column_name")) average.show() ``` 3. 使用Spark Streaming读取Kafka消息并计算单词数量: ```scala val ssc = new StreamingContext(sparkConf, Seconds(1)) val kafkaParams = Map[String, Object]( "bootstrap.servers" -> "localhost:9092", "key.deserializer" -> classOf[StringDeserializer], "value.deserializer" -> classOf[StringDeserializer], "group.id" -> "group_id", "auto.offset.reset" -> "latest", "enable.auto.commit" -> (false: java.lang.Boolean) ) val topics = Array("topic_name") val stream = KafkaUtils.createDirectStream[String, String]( ssc, PreferConsistent, Subscribe[String, String](topics, kafkaParams) ) val words = stream.flatMap(record => record.value().split(" ")) val wordCounts = words.map(word => (word, 1)).reduceByKey(_ + _) wordCounts.print() ssc.start() ssc.awaitTermination() ``` 这些示例代码可以帮助您了解如何使用Spark进行基本操作。对于更复杂的企业案例,您需要根据具体情况编写代码
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值