一般是通过SparkSession 导入数据来创建的。dataframe可以通过访问文件系统或创建RDD来获得。
创建RDD来生成dataframe数据:
调用spark.read.json()方法可以实现RDD到dataframe的转换,而且是直接执行的,但是创建临时表createOrReplaceTempView() 操作只有执行到dataframe动作时,创建临时表才会执行。
spark 中的dataframe与pandas中的dataframe具有较大的不同,spark中的dataframe除了支持api调用操作之外,还支持通过编写SQL的方法执行操作。
2.1 查询:
# 通过API查询 --> df.show()
# 通过编写SQL查询 --> spark.sql('select * from df').show() 可定义参数num控制返回数据大小
2.2 指定dataframe的字段类型
指定模式(手动指定) & 反推断模式(默认指定)
调用printSchema() 方法查看dataframe的数据结构;指定字段的数据类型就需要调用StructType和StructField(通过Spark Sql 来引入)
2.3 筛选查询操作
dataframe方法进行筛选,也可用SQL语句。
#使用自带的方法 student.select('age', 'name', 'id').filter('id == 1').show()
# 或者 student.select(student.age, student.id).filter(student.id==1).show()
#SQL查询 spark.sql('select * from student where id=1').show()
2.4 查
# 查行数 df.count()
# 取别名 df.select(df.age.alias('age_v'),'name')
# 查询某列为null的行 from pyspark.sql.functions import isnull
df = df.filter(isnull("col_a"))
#查询唯一值 df.select('columns').distinct().show()
#获取元素列名 Row(age=11, name='Alice').columns
#选择多列 df["name"] 或 df.name; df.select(“name”) ;df.select(df[‘name’], df[‘age’]+1) ;
df.select(df.a, df.b, df.c) 或 df.select(df["a"], df["b"], df["c"])
#select方法重载 df.select(df( "id" ), df( "id") + 1 ).show( false) 同时显示id列和id+1列
#排序 df.orderBy(df.age.desc()).show(5)
#when和between from pyspark.sql import functions as F
df.select(df.name, F.when(df.age > 4, 1).when(df.age < 3, -1).otherwise(0)).show()
df.select(df.name, df.age.between(22, 24)).show()
2.5 增
新增DataFrame
新增列 df.withColumn('Purchase_new', df.Purchase /2.0).select('Purchase','Purchase_new').show(5)
修改列名 df.withColumnRenamed( "id" , "idx" )
2.6 删——删除列、空值,填充NA,去重
#删除列 df.drop('age').collect() 或者 df.drop(df.age).collect()
#删除空值 df = df.na.drop() # 删除任何列包含na的行 ; df = df.dropna(subset=['col1', 'col2']) # 删掉col1或col2中任一一列包含na的行
#填充NA df.fillna(-1)
#去重 df.distinct() #返回不包含重复行的dataframe ; df.select('Age','Gender').dropDuplicates().show() # 指定字段去重
join join 操作与pandas中的merge操作相似,需要注意拼接时坐标为主还是右表为主,是内连接还是外连接。
并、交、差、去重 差集:subtract(),交集:intersect(),并集:union()就可以实现dataframe里的数据的比较。
#差集 newDF = sentenceDF1.select("sentence").subtract(sentenceDF.select("sentence")) newDF.show()
#交集 newDF = sentenceDF1.select("sentence").intersect(sentenceDF.select("sentence")) newDF.show()
#并集 newDF = sentenceDF1.select("sentence").union(sentenceDF.select("sentence")) newDF.show()
#并集+去重 newDF = sentenceDF1.select("sentence").union(sentenceDF.select("sentence")).distinct() newDF.show()
分割:行转列 根据某个字段内容进行分割,然后生成多行
#分割 df.explode( "c3" , "c3_" ){time: String => time.split( " " )}
统计
# 统计频数 df.stat.freqItems(Seq ("c1") , 0.3).show()
# 交叉分析 df.crosstab('Age', 'Gender').show()
# groupby()方法 df.groupby('Age').agg({'Purchase': 'mean'}).show()
# 分组汇总 df.groupby('Age').count().show()
# 混合应用 from pyspark.sql import functions
df.groupBy(“A”).agg(functions.avg(“B”), functions.min(“B”), functions.max(“B”)).show()
整合后GroupedData类型可用的方法(均返回DataFrame类型):
avg(*cols) 计算每组中一列或多列的平均值
count() 计算每组中一共有多少行,返回DataFrame有2列,一列为分组的组名,另一列为行总数
max(*cols) 计算每组中一列或多列的最大值
mean(*cols) 计算每组中一列或多列的平均值
min(*cols) 计算每组中一列或多列的最小值
sum(*cols) 计算每组中一列或多列的总和
函数应用
# 每一列应用函数 df.foreach(f) 或 df.rdd.foreach(f)
# 每一块应用函数 df.foreachPartition(f) 或 df.rdd.foreachPartition(f)
pandas与pyspark中的dataframe 相互转换
# spark 转换为pandas的dataframe pandas_df = spark_df.toPandas()
# pandas的dataframe转化为spark的dataframe spark_df = sc.createDataFrame(pandas_df)
df与RDD的相互转换
# DF转换为RDD rdd_df = df.rdd
# RDD转换为DF df = rdd_df.toDF()
DataFrame注册为临时表
df.createOrReplaceTempView("df")
df.registerTempTable("hello")
DateFrame随机取样
DataFrame.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
n: 要抽取的行数,需为整数值
frac:抽取的比列,需为小数值,比方说我们想随机抽取30%的数据,则设置frac=0.3即可。
replace:抽样后的数据是否代替原DataFrame(),默认为False
weights:默认为等概率加权
random_state:随机种子,本质是一个控制器,设置此值为任意实数,则每次随机的结果是一样的
axis:抽取数据的行还是列,axis=0的时是抽取行,axis=1时是抽取列
缺失值处理
查找缺失值
# 查找每行缺失的数据
df.rdd.map(lambda row: (row['id'], sum([c == None for c in row]))).collect()
这里先统计了每一行的缺失值个数,可以发现第三行缺失值个数最多,是4个,打印查看一下是否正确
df.where('id == 3').show()
处理缺失值
对缺失值的处理有两种策略:删除和填充。对于缺失值占比较大的数据,一般直接移除,对数据的总体影响不会太大
# 查看缺失值占比
import pyspark.sql.functions as fn
df.agg(*[(1- (fn.count(c)/ fn.count('*'))).alias(c + 'missing') for c in df.columns]).show()
# count('*') 表示统计列的所有行数
# agg(*[]) 将该列表作为一组独立的参数传递给函数,可以理解为*arg
从结果可知,income列空值占比60%,所以我们可以删掉这一列
df_no_income = df.select([c for c in df.columns if c != 'income'])
删除行的重复值
# 删除row 的重复值
df_no_income.dropna(thresh=3).show()
# thresh 允许出现空值的最大次数
第三行删掉了,因为空值个数>=3
填充空值
填充空值可以填充平均数、中间值、最大或者最小值等。
# 计算每一列的平均值,并生成为一个字典
# 计算均值时排除了gender列,因为是字符串,不能计算均值
means = df_no_income.agg(*[fn.mean(c).alias(c) for c in df_no_income.columns if c != 'gender']).toPandas().to_dict('records')[0]
# 填充‘gender’ 列的空值
means['gender'] = 'missing'
df_no_income.fillna(means).show()
这里有几个新知识点:
fillna() 参数接收一个字典,或者是单个值;
toPandas() 是把spark的df转换为pandas的df;
to_dict()是把df转换为字典,而参数 records 则指定了如下的格式:{'id':3.0, 'weight':142.25, 'height':5.7250000000000005, 'age':44.0, 'gender': 'missing'}
离群值
离群值:与样本数据显著偏离的数据,这些数据的存在,对机器学习建立模型是非常不友好的,所以在数据建模之前,会对这些数据做些处理,一般直接将离群值舍弃。
离群值的阈值选择有多种方式,均值、四分位数等等,最普遍的做法是下分位(Q1) - 1.5个分位差和上分位(Q3)+1.5个分位差,即:Q1 - 1.5IQR 和Q3 + 1.5IQR,其中IQR为Q3-Q1,Q3为第75个百分点,Q1为第25个百分点
描述性统计是熟悉数据的最直接的方法之一,可以获知数据中的缺失观测数据、平均值、标准差、最值
# 导包和创建连接的代码略
# 加载数据类型模块
import pyspark.sql.types as typ
# 加载数据
fraud = sc.textFile('./ccFraud.csv')
header = fraud.first()
# 删除标题行 并将每个元素转化为整形
fraud = fraud.filter(lambda row: row != header).map(lambda row: [int(elem) for elem in row.split(',')])
指定DF的数据类型,是通过StructField() 方法和StructType() 方法实现的
fields = [*[typ.StructField(h[1:-1], typ.IntegerType(), True) for h in header.split(',')]]
schema = typ.StructType(fields)
# 创建DF
fraud_df = spark.createDataFrame(fraud, schema)
对数据进行统计性描述
## 使用describe()方法 对数据进行统计性描述
numerical = ['balance', 'numTrans', 'numIntlTrans']
desc = fraud_df.describe(numerical)
desc.show()
从以上数据中 可以看出:最大值是平均值的多倍,说明数据呈正偏态分布; 均值和标准差之比非常高(接近或者大于1)说明这是一个广泛的数据集
检查数据的相关性:
数据的相关性,pyspark中一般用corr()方法计算相关性,缺陷是只能计算两两的相关性。
n_numerical = len(numerical)
# 查看相关性
corr = [ ]
for i in range(0, n_numerical):
temp = [None] *i
for j in range(i , n_numerical):
temp.append(fraud_df.corr(numerical[i], numerical[j]))
corr.append(temp)