pyspark系列--datafrane進階

datafrane進階

1. 分組統計

分組統計應該是用的最多的方法了,比如分地區求平均值,最大最小值等。

# 分組計算1
color_df.groupBy('length').count().show()

# 分組計算2:應用多函數
import pyspark.sql.functions as func
color_df.groupBy("color").agg(func.max("length"), func.sum("length")).show()

2. join 操作

# 1.生成測試數據
employees = [(1, "John", 25), (2, "Ray", 35), (3,"Mike", 24), (4, "Jane", 28), (5, "Kevin", 26), 
             (6, "Vincent", 35), (7,"James", 38), (8, "Shane", 32), (9, "Larry", 29), (10, "Kimberly", 29),
             (11, "Alex", 28), (12, "Garry", 25), (13, "Max",31)]
employees=spark.createDataFrame(employees, schema=["emp_id","name","age"])
employees.show()

salary=[(1,1000),(2,2000),(3,3000),(4,4000)]
salary=spark.createDataFrame(salary, schema=["emp_id","salary"])
salary.show()

department=[(1,1000),(2,2000),(3,3000),(4,4000)]
department=spark.createDataFrame(department, schema=["emp_id","departement"])
department.show()

# 2.連接
# join默認是內連接,最終結果會存在重複列名
# 如果是pandas,重複列會用_x,_y等後綴標識出來,但spark不會
# join會在最後的dataframe中存在重複列

final_data = employees.join(salary, employees.emp_id == salary.emp_id, how='left')\
    .join(department, employees.emp_id==department.emp_id)
final_data.show()

# 3.如果兩邊的關聯字段名相同,也可以省去很多麻煩
final_data = employees.join(salary, on='emp_id', how='left')\
    .join(department, on='emp_id', how='left')
final_data.show()

# 4.多字段關聯
final_data = employees.join(salary, on=['emp_id','emp_name'], how='left')

# 5.外連接
# full outer join全外連接 
df.join(df2, "name", "outer").select("id", "name", "age", "height").orderBy("id").show()

3. 缺失值處理

在join操作中,我們得到一個有缺失值的dataframe,接下來將對這個帶有缺失值的dataframe進行操作

# 1.刪除有缺失值的行
clean_data=final_data.na.drop()
clean_data.show()

# 2.用均值替換缺失值
import math
from pyspark.sql import functions as func  # 導入spark內置函數
# 計算缺失值,collect()函數將數據返回到driver端,爲Row對象,[0]可以獲取Row的值
mean_salary = final_data.select(func.mean('salary')).collect()[0][0]
clean_data = final_data.na.fill({'salary':mean_salary})

# 3.如果一行至少2個缺失值才刪除該行
final_data.na.drop(thresh=2).show()

# 4.填充缺失值
# 對所有列用同一個值填充缺失值
df1.na.fill('unknown').show()

# 5.不同的列用不同的值填充
df1.na.fill({'LastName':'--', 'Dob':'unknown'}).show()

4. 空值判斷

有兩種空值判斷,一種是數值類型是nan,另一種是普通的None

# 類似 pandas.isnull

from pyspark.sql.functions import isnull, isnan

# 1.None 的空值判斷
df = spark.createDataFrame([(1, None), (None, 2)], ("a", "b"))
df.select(isnull("a").alias("r1"), isnull(df.a).alias("r2")).show()

# 2.nan的空值判斷
df = spark.createDataFrame([(1.0, float('nan')), (float('nan'), 2.0)], ("a", "b"))
df.select(isnan("a").alias("r1"), isnan(df.a).alias("r2")).show()

5. 缺失值處理

如果col1爲空則用col2填補,否則返回col1。
類似 pandas 的 where 或者 combine_first 方法

# pandas 
#where即if-else函數
np.where(isnull(a),b,a)

# combine_first方法
#如果a中值爲空,就用b中的值填補
a[:-2].combine_first(b[2:])

#combine_first函數即對數據打補丁,用df2的數據填充df1中的缺失值
df1.combine_first(df2)


# pyspark
df = spark.createDataFrame([(1.0, float('nan')), (float('nan'), 2.0)], ("a", "b"))
df.select(nanvl("a", "b").alias("r1"), nanvl(df.a, df.b).alias("r2")).show()

6. 離羣點

# 需要提醒的是,列的計算都是放在select裏面的

# 1.先計算均值
mean_salary = final_data.select(func.mean('salary')).collect()[0][0]
# 2.再計算方差
devs=final_data.select(((final_data.salary-mean_salary)**2).alias('deviation'))
# 3.再計算標準差
stddev = math.floor(math.sqrt(devs.groupBy().avg('deviation').first()[0]))

# 4.用均值的兩倍標準差替代離羣值
no_outlier = final_data.select(
    final_data.emp_id, final_data.name, final_data.age, final_data.salary,
    func.when(final_data.salary.between(mean_salary-2*stddev, mean_salary+2*stddev), final_data.salary)
        .otherwise(mean_salary)
        .alias("updated_salary")
    )
no_outlier.show()


# func中有現成的常用統計函數,更加方便
# 1.計算均值
mean_salary = final_data.select(func.mean('salary')).collect()[0][0]
# 2.計算標準差
final_data.select(func.stddev('salary')).collect()[0][0]
# 離羣值替代就和上面的一致了

7. 重複值

# 重複值的處理,和pandas很像啊
authors = [['Thomas','Hardy','June 2,1840'],
            ['Thomas','Hardy','June 2,1840'],
            ['Thomas','H',None],
            ['Jane','Austen','16 December 1775'],
            ['Emily',None,None]]

df1 = spark.createDataFrame(authors,schema=["FirstName","LastName","Dob"])
df1.show()

# 刪除重複值行
df1.dropDuplicates().show()

# 只要某一列有重複值,則去重
df1.dropDuplicates(subset=['FirstName']).show()


# pandas的方法
df=pd.DataFrame(authors, columns=["FirstName","LastName","Dob"])
df.drop_duplicates(subset=['FirstName'])

8. 生成新列

# 數據轉換,可以理解成列與列的運算
# 注意自定義函數的調用方式

# 0.創建udf自定義函數,對於簡單的lambda函數不需要指定返回值類型
from pyspark.sql.functions import udf
concat_func = udf(lambda name,age:name+'_'+str(age))

# 1.應用自定義函數
concat_df = final_data.withColumn("name_age", concat_func(final_data.name, final_data.age))
concat_df.show()

# 2.通過列生成另一列
data_new=concat_df.withColumn("age_incremented",concat_df.age+1)
data_new.show()

# 3.某些列是自帶一些常用的方法的
df1.withColumn('Initial', df1.LastName.substr(1,1)).show()

# 4.順便增加一新列
from pyspark.sql.functions import lit
df1.withColumn('newCol', lit(0)).show()

9. 類eval操作

傳入一個操作字符串,然後轉成python代碼執行,就像python的eval一樣。

from pyspark.sql.functions import expr
color_df.select(expr('length(color)')).show()

10. 行的最大最小值

求每一行的最大最小值,相當於axis=1

# 測試數據
df=[(1,1000),(2,2000),(3,3000),(4,4000)]
df=spark.createDataFrame(df, schema=["emp_id","salary"])
df.show()

# 求行的最大最小值
from pyspark.sql.functions import greatest, least
df.select(greatest('emp_id','salary').alias('greatest'),
          least('emp_id','salary').alias('least')
          ).show()
# +--------+-----+
# |greatest|least|
# +--------+-----+
# |    1000|    1|
# |    2000|    2|
# |    3000|    3|
# |    4000|    4|
# +--------+-----+

11. when操作

相當於SQL中的case when 操作

from pyspark.sql.functions import when

# 1.case when age=2 then 3 else 4
df.select(when(df['age'] == 2, 3).otherwise(4).alias("age"))show()

# 2.case when age=2 when age=age+1 
df.select(when(df.age == 2, df.age + 1).alias("age")).show()

case when age<2 then age+2 else age end
df.withColumn('age', when(df.age == 2, df.age + 1).otherwise(df2['age'])).show()

12. lag,lead平移

很好用的函數啊,特別是在處理時間序列的時候,和pandas的shift很像。

from pyspark.sql.functions import lag, lead
df = spark.createDataFrame([(1, 2, 3) if i % 2 == 0 else (i, 2 * i, i % 4) for i in range(5)],
                           ["a", "b", "c"])
df.show()

df.select(lag('a', 1, 0).alias('lag')).show()
df.select(lead('a', 1, 0).alias('lag')).show()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章