一、認識資料處理
為什麼要預處理資料?
1、現實世界的資料是“肮髒的”——資料多了,什麼問題都會出現
- 不完整的:缺少屬性值,缺少感興趣的屬性,或僅包含聚集資料, 如:e.g., Occupation=“”
- 含噪聲的:包含錯誤或者“孤立點”, e.g., Salary=“-10”
- 不一緻的:在編碼或者命名上存在差異,E.g. Age=“42” Birthday=“03/07/1997” 如:等級代碼 前面“1,2,3”, 後面“A, B, C”
2、沒有高品質的資料,就沒有高品質的挖掘結果
- 高品質的決策必須依賴高品質的資料
為什麼資料是肮髒?
1、不完整資料來自:
- 感興趣的資料難以獲得
- 資料收集時與做資料分析時側重的問題不一緻
- 人/硬體/軟體問題
2、噪音資料來自資料處理過程:
- 收集–>錄入–>傳輸
3、不一緻資料來自:
- 不同資料源
- 資料違背了依賴關系
資料處理的主要任務
1、資料內建
內建多個資料庫、資料立方體或檔案
2、資料清理
- 填充缺失值
- 識别孤立點,去除噪音
- 修正不一緻資料
- 解決由于資料內建造成的資料備援問題
3、資料規約
得到資料集的壓縮表示,它小得多,但可以得到相同或相近的結果,包括維規約和數值規約
4、資料變換
規範化:将資料按比例縮放,使之落入一個小的特定區間
5、資料離散化
離散化:數值屬性的原始值用區間标簽或概念标簽替換,或者标稱屬性轉化為數值屬性
二、資料內建
1、橫向堆疊-concat
橫向堆疊,即将兩個表在 X 軸向拼接在一起,可以使用 concat 函數完成,concat 函數 的基本文法如下:
pandas.concat(objs,axis=0,join=‘outer’,join_axes=None,ignore_index=False,keys=None,levels=None, names=None, verify_integrity=False, copy=True)
concat 參數說明表
參數名稱 | 說明 |
---|---|
objs | 接收多個 Series,DataFrame,Panel 的組合,表示參與連結的 pandas 對象的清單 的組合,無預設, axis |
join | 接收 inner 或 outer。表示其他軸向上的索引是按交集(inner)還是并集(outer) 進行合并,預設為 outer |
join_axes | 接收 Index 對象,表示用于其他 n-1 條軸的索引,不執行并集/交集運算 |
ignore_index | 接收 boolean,表示是否不保留連接配接軸上的索引,産生一組新索引 ignore_index range(total_length),預設為 False |
keys | 接收 sequence,表示與連接配接對象有關的值,用于形成連接配接軸向上的階層化索引,默 keys 認為 None |
levels | 接收包含多個sequence 的list,表示在指定 keys 參數後,指定用作階層化索引各級别上的索引,預設為 None |
names | 接收 list,表示在設定了 keys 和 levels 參數後,用于建立分層級别的名稱,預設為None |
verify_integrity | 接收 boolearn,表示是否檢查結果對象新軸上的重複情況,如果發現則引發異常,預設為 False |
當 axis=1 的時候,concat 做行對齊,然後将不同列名稱的兩張或多張表合并,當兩個 表索引不完全一樣時,可以使用 join 參數選擇是内連接配接還是外連接配接,在内連接配接的情況下, 僅僅傳回索引重疊部分,在外連接配接的情況下,則顯示索引的并集部分資料,不足的地方則使用空值填補
當兩張表完全一樣時,不論 join 參數取值是 inner 或者 outer,結果都是将兩個表完 全按照 X 軸拼接起來
2、縱向堆疊-concat
使用 concat 函數時,在預設情況下,即 axis=0 時,concat 做列對齊,将不同行索引 的兩張或多張表縱向合并,在兩張表的列名并不完全相同的情況下,可 join 參數取值為 inner 時,傳回的僅僅是列名交集所代表的列,取值為 outer 時,傳回的是兩者列名的并集 所代表的列,其原理示意如圖
不論 join 參數取值是 inner 或者 outer,結果都是将兩個表完全按照 Y 軸拼接起來
代碼實作:
import pandas as pd
# 加載資料
df1 = pd.read_excel('./concat直接拼接資料.xlsx', sheet_name=0, index_col=0)
print('df1:\n', df1)
df2 = pd.read_excel('./concat直接拼接資料.xlsx', sheet_name=1, index_col=0)
print('df2:\n', df2)
# 可以是pd.concat來進行橫向堆疊
# 當axis=1,join= 'outer'
# 在列的方向直接拼接,在行的方向,拿取所有的行,在沒有資料的行的位置用NaN來補齊
res = pd.concat((df1, df2), axis=1, join='outer')
print('res:\n',res)
# 當axis =1 ,join = 'inner'
# 在列的方向上直接拼接,在行的方向,取共同擁有的行
res = pd.concat((df1, df2), axis=1, join='inner')
print('res:\n', res)
# pd.concat來進行縱向堆疊
# 當axis=0, join = 'outer'
# 在行的方向,直接拼接,在列的方向,拿取所有的列,在沒有值的列的位置用NaN來補齊
res = pd.concat((df1, df2), axis=0, join='outer', sort=False)
print('res:\n', res)
# 當axis=0,join='inner'
# 在行的方向,直接拼接,在列的方向,拿取共同擁有的列
res = pd.concat((df1, df2), axis=0, join='inner', sort=False)
print('res:\n', res)
3、縱向堆疊-append
append 方法也可以用于縱向合并兩張表。但是 append 方法實作縱向表堆疊有一個前提 條件,那就是兩張表的列名需要完全一緻。append 方法的基本文法如下
pandas.DataFrame.append(self, other, ignore_index=False, verify_integrity=False)
append 參數說明表
參數名稱 | 說明 |
---|---|
other | 接收 DataFrame 或 Series。表示要添加的新資料,無預設 |
ignore_index | 接收 boolean,如果輸入 True,會對新生成的 DataFrame 使用新的索引(自動産 生)而忽略原來資料的索引,預設為 False |
verify_integrity | 接收 boolean,如果輸入 True,那麼當 ignore_index 為 False 時,會檢查添加的 資料索引是否沖突,如果沖突,則會添加失敗,預設為 False |
代碼實作:
# 也可以使用append來進行縱向堆疊
df1 = pd.read_excel('./append直接拼接資料.xlsx', sheet_name=0, index_col=0)
print('df1:\n', df1)
df2 = pd.read_excel('./append直接拼接資料.xlsx', sheet_name=1, index_col=0)
print('df2:\n', df2)
# 和 pd.concat 的axis=0,join='outer' 相同,一般情況下,要求在列名相同時使用
res = df1.append(df2, sort=False)
print('res:\n', res)
三、案例:垂釣裝備合并案例
近年來,随着人們的生活水準的提高,人們對于精神生活品質的要求越來越高,于是, 各種娛樂活動出現,其中,釣魚屬于一種戶外運動,目标是用漁具把魚從水裡釣上來,而且 釣魚不限制性别與年齡,大人小孩子都喜歡,釣魚親近大自然,陶冶情操,然而在釣魚的時 候,釣魚裝備又影響着釣魚的成果,是以,越來越多的釣友對于釣魚裝備的要求越來越高, 對于釣魚裝備的需求也越來越多,那麼對于釣魚裝備的銷售公司來說,了解客戶對于不同品牌的釣魚裝備的喜愛程度,對于銷售公司來說至關重要
以下為某釣魚裝備公司的銷售記錄,針對于銷售記錄,來确定客戶最喜愛的品牌或者最火品牌
以下為每類釣魚裝備銷售記錄細表(表結構類似):
釣魚裝備&抄網銷售記錄細表(部分)
日期 | 轉化率 | 訪客數 | 三級類目 | 客單價 | 品牌 |
---|---|---|---|---|---|
2019-08 | 0.075437 | 448725 | 抄網 | 28.347 | 品牌-7 |
2019-08 | 0.066758 | 303676 | 抄網 | 35.92695 | 品牌-1 |
2019-08 | 0.029469 | 253084 | 抄網 | 65.60413 | 品牌-5 |
2019-08 | 0.032616 | 166098 | 抄網 | 82.162 | 品牌-10 |
2019-08 | 0.080396 | 173662 | 抄網 | 31.35206 | 品牌-13 |
釣魚裝備&釣竿銷售記錄細表(部分)
日期 | 轉化率 | 訪客數 | 三級類目 | 客單價 | 品牌 |
---|---|---|---|---|---|
2019-08 | 0.029797 | 3939046 | 釣竿 | 135.6066 | 品牌-3 |
2019-08 | 0.027249 | 1948598 | 釣竿 | 254.9653 | 品牌-19 |
2019-08 | 0.031458 | 829839 | 釣竿 | 478.2628 | 品牌-16 |
2019-08 | 0.017834 | 938215 | 釣竿 | 670.2237 | 品牌-2 |
2019-08 | 0.00864 | 522226 | 釣竿 | 1729.252 | 品牌-18 |
到底如何了解最火品牌?其實可以轉化為确定哪個品牌的銷售額最高,那麼該品牌就是最火的品牌,會發現在各個類别的銷售記錄細表中,并不存在着銷售額特征字段,但是可以通過“銷售額=通路數 * 轉化率乘于 客單價”來确定銷售額
代碼實作:
import pandas as pd
import os
import time
# 開始
start = time.time()
# # 以綁鈎器 為例,來先研究 不同的品牌 跟銷售額之間的關系
#
excel_name = '垂釣裝備&綁鈎器.xlsx'
# # 加載資料
data = pd.read_excel('./data/' + excel_name)
# print('data:\n', data)
# print('data:\n', data.columns)
# 計算銷售額
# 銷售額 = 訪客數 * 轉化率 * 客單價
data.loc[:, '銷售額'] = data.loc[:, '訪客數'] * data.loc[:, '轉化率'] * data.loc[:, '客單價']
print('data:\n', data)
print('data:\n', data.columns)
# 按照品牌進行分組,統計 銷售額的 sum
res = pd.pivot_table(data=data,
index='品牌',
values='銷售額',
aggfunc='sum').reset_index().sort_values(by='銷售額', ascending=False)
# 可以添加行業
res['行業'] = excel_name.split('.')[0]
print('res:\n', res)
# 以整個 垂釣裝備 為例
# 擷取所有檔案名稱
file_list = os.listdir('./data/')
print('file_list:\n', file_list)
# 建立一個空的df
res = pd.DataFrame()
# 周遊
for excel_name in file_list:
# print('excel_name:\n', excel_name) # 代表是不同的裝備excel
# 對 每一個excel 進行操作
data = pd.read_excel('./data/' + excel_name, sheet_name=0)
# print('data:\n', data)
# print('data:\n', data.columns)
# 計算其 營業額
data.loc[:, '營業額'] = data.loc[:, '訪客數'] * data.loc[:, '轉化率'] * data.loc[:, '客單價']
# 按照品牌進行分組,統計 營業額的 sum
df = pd.pivot_table(data=data,
index='品牌',
values='營業額',
aggfunc='sum').reset_index()
# 增加行業
df.loc[:, '行業'] = excel_name.split('.')[0]
# print('df:\n', df)
# 合并 --縱向堆疊
# ignore_index=True --->無視原來的行索引,重新生成新的行索引
res = pd.concat((res, df), axis=0, join='outer', ignore_index=True)
# print('res:\n',res)
print('res:\n', res)
# 按照 品牌進行分組, 統計營業額的 sum
res_data = pd.pivot_table(data=res,
index='品牌',
values='營業額',
aggfunc='sum').reset_index().sort_values(by='營業額', ascending=False).head(5)
# 設定pandas的顯示
pd.set_option('display.float_format', lambda x: '%.2f' % x)
print('res_data:\n', res_data)
print('總共花費的時間為:\n', time.time() - start)
四、主鍵合并-megre
主鍵合并,即通過一個或多個鍵将兩個資料集的行連接配接起來,類似于 SQL 中的 JOIN。,針對同一個主鍵存在兩張包含不同字段的表,将其根據某幾個字段一一對應拼接起來,結果 集列數為兩個中繼資料的列數和減去連接配接鍵的數量
想要進行左右兩表的主鍵合并,可以使用 megre 函數,和資料庫的 join 一樣,merge 函數也有左連接配接(left)、右連接配接(right)、内連接配接(inner)和外連接配接(outer),但比起數 據庫 SQL 語言中的 join 和 merge 函數還有其自身獨到之處,例如可以在合并過程中對資料集中的資料進行排序等
pandas.merge(left,right,how=‘inner’,on=None,left_on=None,right_on=None,left_index=False,right_index=False,sort=False, suffixes=(’_x’, ‘_y’), copy=True, indicator=False)
可根據 merge 函數中的參數說明,并按照需求修改相關參數,就可以多種方法實作主鍵合并
merge 參數說明表
參數名稱 | 說明 |
---|---|
left | 接收 DataFrame 或 Series。表示要添加的新資料,無預設 right |
on | 接收 string 或 sequence,表示兩個資料合并的主鍵(必須一緻),預設為 None |
left_on | 接收 string 或 sequence,表示 left 參數接收資料用于合并的主鍵,預設為 None |
right_on | 接收 string 或 sequence。表示 right 參數接收資料用于合并的主鍵,預設為 None |
left_index | 接收 boolean,表示是否将 left 參數接收資料的 index 作為連接配接主鍵,預設為 False |
right_index | 接收 boolean,表示是否将 right 參數接收資料的 index 作為連接配接主鍵,預設為 False |
sort | 接收 boolean,表示是否根據連接配接鍵對合并後的資料進行排序,預設為 False |
suffixes | 接收 tuple 表示用于追加到 left 和 right 參數接收資料重疊列名的尾綴預設為 (’_x’, ‘_y’) |
代碼實作:
import pandas as pd
# 可以使用 pd.merge 來實作主鍵連接配接,類似mysql資料庫中的主鍵連接配接
# 加載資料
df1 = pd.read_excel('./merge主鍵拼接資料.xlsx', sheet_name=0, index_col=0)
print('df1:\n', df1)
df2 = pd.read_excel('./merge主鍵拼接資料.xlsx', sheet_name=1, index_col=0)
print('df2:\n', df2)
# 主鍵合并
# how= inner, 擷取 key 列 共同擁有的值進行左右連接配接
res = pd.merge(left=df1, right=df2, how='inner', on='key')
print('res:\n', res)
# how= outer ,擷取key列所有的值進行左右連接配接,如果沒有對應的行,用NaN來補齊
res = pd.merge(left=df1, right=df2, how='outer', on='key')
print('res:\n', res)
# how=left 擷取左表中key中的所有的值進行連接配接,用右表來配合左表,右表中沒有的值用NaN來補齊
res = pd.merge(left=df1, right=df2, how='left', on='key')
print('res:\n', res)
# how= right 擷取右表中key中所有的值進行連接配接,用左表來配合右表,左表中沒有的值用NaN來補齊
res = pd.merge(left=df1, right=df2, how='right', on='key')
print('res:\n', res)
# 當左表兩表中的鍵名稱不相同,但是裡面的值大部分相同時的連接配接
df1 = pd.read_excel('./merge主鍵拼接資料.xlsx', sheet_name=2, insheet_namedex_col=0)
print('df1:\n', df1)
#
df2 = pd.read_excel('./merge主鍵拼接資料.xlsx', sheet_name=3, index_col=0)
print('df2:\n', df2)
df3 = pd.read_excel('./merge主鍵拼接資料.xlsx', sheet_name=4, index_col=0)
print('df3:\n', df3)
# 當how = inner, 使用kx 與ky中共同擁有的值進行連接配接
res = pd.merge(left=df1, right=df2, how='inner', left_on='Kx', right_on='Ky')
print('res:\n', res)
# 當how = outer 使用kx 與ky中所有的值來進行連接配接,沒有值的位置用NaN來補齊
res = pd.merge(left=df1, right=df2, how='outer', left_on='Kx', right_on='Ky')
print('res:\n', res)
# 當how = left, 使用kx中所有的值來進行連接配接,右表配合左表,右表中沒有值的位置用NaN來補齊
res = pd.merge(left=df1, right=df2, how='left', left_on='Kx', right_on='Ky')
print('res:\n', res)
# # how=right 使用ky中所有的值來進行連接配接,左表配合右表,左表中沒有值的位置用NaN來補齊
res = pd.merge(left=df1, right=df2, how='right', left_on='Kx', right_on='Ky')
print('res:\n', res)
# 當左右兩表存在相同名稱列,且為非鍵列,合并之後為了區分,會加上_x,_y來進行區分左右兩表的相同名稱列
res = pd.merge(left=df1, right=df3, how='right', left_on='Kx', right_on='Ky')
print('res:\n', res)
# 多個主鍵連接配接
# 當這多個主鍵 都相同時,才進行連接配接
df1 = pd.read_excel('./merge主鍵拼接資料.xlsx', sheet_name=5, index_col=0)
print('df1:\n', df1)
df2 = pd.read_excel('./merge主鍵拼接資料.xlsx', sheet_name=6, index_col=0)
print('df2:\n', df2)
#
res = pd.merge(left=df1, right=df2, on=['kk', 'kg'], how='outer')
print('res:\n', res)
# 還有jion
# df1.join(df2) # --->主鍵拼接
六、重疊合并
資料分析和處理過程中若出現兩份資料的内容幾乎一緻的情況,但是某些特征在其中一 張表上是完整的,而在另外一張表上的資料則是缺失的時候,可以用 combine_first 方法進行 重疊資料合并,其原理如下
combine_first 的具體用法如下:
pandas.DataFrame.combine_first(other)
combine_first 參數說明表
參數名稱 | 說明 |
---|---|
other | 接收 DataFrame,表示參與重疊合并的另一個 DataFrame,無預設 |
代碼實作:
import pandas as pd
# 使用資料表格來填充另外一個資料
# 加載資料
df1 = pd.read_excel('./重疊合并資料.xlsx', sheet_name=0, index_col=0)
print('df1:\n', df1)
df2 = pd.read_excel('./重疊合并資料.xlsx', sheet_name=1, index_col=0)
print('df2:\n', df2)
# 對應位置進行填充,如有存在,并不會覆寫
res = df1.combine_first(df2)
print('填充之後的結果為:\n', res)
七、案例:合并訂單、使用者資料
将訂單詳情表、訂單資訊表、使用者資訊表進行合并,組成一個完整的資料集,
代碼實作:
import pandas as pd
# 加載detail
detail_1 = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
detail_2 = pd.read_excel('./meal_order_detail.xlsx', sheet_name=1)
detail_3 = pd.read_excel('./meal_order_detail.xlsx', sheet_name=2)
# 合并detail
detail = pd.concat((detail_1, detail_2, detail_3), axis=0, join='inner')
print('detail:\n', detail)
print('detail:\n', detail.columns)
# 加載info
info = pd.read_csv('./meal_order_info.csv', encoding='ansi')
print('info:\n', info)
print('info:\n', info.columns)
# 加載users
users = pd.read_excel('./users.xlsx', sheet_name=0)
print('users:\n', users)
print('users:\n', users.columns)
# 主鍵合并
# detail 中 order_id 與info 中 info_id
# detail 中 emp_id 與info 中 emp_id
df = pd.merge(left=detail, right=info, how='inner', left_on=['order_id', 'emp_id'], right_on=['info_id', 'emp_id'])
print('df:\n', df)
print('df:\n', df.shape)
print('df:\n', df.columns)
# 與 users再進行主鍵合并
# df中的 name 和 users ACCOUNT
df = pd.merge(left=df, right=users, left_on='name', right_on='ACCOUNT', how='inner')
# print('df:\n', df)
# print('df:\n', df.shape)
# print('df:\n', df.columns)
# name,ACCOUNT 相同
# order_id,info_id 相同
df.drop(labels=['ACCOUNT', 'info_id'], axis=1, inplace=True)
# print('df:\n', df)
# print('df:\n', df.shape)
# print('df:\n', df.columns)
# 删除整列為空的資料
drop_list = []
for column in df.columns:
# 判斷
if df.loc[:, column].count() == 0:
# 添加
drop_list.append(column)
# 删除整列為空的資料
df.drop(labels=drop_list, axis=1, inplace=True)
# print('df:\n', df)
# print('df:\n', df.shape)
# print('df:\n', df.columns)
drop_list = []
# 删除整列都是一樣資料的列
for column in df.columns:
# 去重
res = df.drop_duplicates(subset=column, inplace=False)
# 判斷
if res.shape[0] == 1:
drop_list.append(column)
# 删除整列資料重複的列
df.drop(labels=drop_list, axis=1, inplace=True)
print('df:\n', df)
print('df:\n', df.shape)
print('df:\n', df.columns)
八、案例:神奇數字
在 1973 年,統計學家 F.J. Anscombe 造了四組非常神奇的數字,這四組數字具體有多 神奇呢?就是均值、方差、相關性都一樣,但是分布卻完全不一樣
1、加載資料,格式調整
代碼實作:
import pandas as pd
import matplotlib.pyplot as plt
# 加載資料
data = pd.read_csv('./anscombe.csv')
print('data:\n', data)
# 拆分
df1 = data.loc[data.loc[:, 'dataset'] == 'I', :].reset_index().drop(labels=['index', 'dataset'], axis=1, inplace=False)
print('df1:\n', df1)
df2 = data.loc[data.loc[:, 'dataset'] == 'II', :].reset_index().drop(labels=['index', 'dataset'], axis=1, inplace=False)
print('df2:\n', df2)
df3 = data.loc[data.loc[:, 'dataset'] == 'III', :].reset_index().drop(labels=['index', 'dataset'], axis=1,
inplace=False)
print('df3:\n', df3)
df4 = data.loc[data.loc[:, 'dataset'] == 'IV', :].reset_index().drop(labels=['index', 'dataset'], axis=1, inplace=False)
print('df4:\n', df4)
print('*' * 100)
# 主鍵拼接
# left_index=True, right_index=True 主鍵拼接的時候,以左表的index為外鍵,以右表的index為外鍵
df1_2 = pd.merge(left=df1, right=df2, left_index=True, right_index=True, how='inner', suffixes=("_I", "_II"))
print('df1_2:\n', df1_2)
df3_4 = pd.merge(left=df3, right=df4, left_index=True, right_index=True, how='inner', suffixes=("_III", "_IV"))
print('df3_4:\n', df3_4)
print('*' * 100)
all_df = pd.merge(left=df1_2, right=df3_4, left_index=True, right_index=True, how='inner')
print('all_df:\n', all_df)
最後可以得到如下所示的結果資料,這就是四組神奇的數字:
x_I 和 y_I 是一組資料、 x_II 和 y_II 是一組資料, x_III 和 y_III 是一組資料, x_IV 和 y_IV 是一組資料,我們可以看到這四組資料是完全不一樣的
2、進行 describe 統計
在資料分析中,我們平常拿到一個表以後,可能會使用 describe 函數去看不同名額的 一個整體情況,那我們這裡也用 describe()看一下結果:
代碼實作:
# 對all_df進行統計名額
res_describe = all_df.describe()[['x_I', 'x_II', 'x_III', 'x_IV', 'y_I', 'y_II', 'y_III', 'y_IV']]
print('res_describe:\n', res_describe)
可以得到結果:
我們可以看到 x_I、x_II、x_III、x_IV 這四個名額的均值和标準差是完全一樣的,y_I、 y_II、y_III、y_IV 這四個名額的均值和标準差也是完全一樣的(忽略小數點差異),幾列完 全不同的值,均值和标準差竟然會完全一樣
3、計算兩兩之間的相關系數
代碼實作:
# 兩兩之間的相關系數
# x 和 y 的相關系數
res_corr = all_df.corr()
print('res_corr:\n', res_corr)
可以得到名額兩兩之間相關性情況,我們可以看到四組的相關性系數也基本相同(忽略 小數點差異)
4、散點圖可視化
代碼實作:
# 檢視資料分布狀态
# 建立畫布
fig = plt.figure()
# 預設不支援中文 ---修改RC參數
plt.rcParams['font.sans-serif'] = 'SimHei'
# 增加字型之後變得不支援負号,需要修改RC參數讓其繼續支援負号
plt.rcParams['axes.unicode_minus'] = False
# 調整間距
plt.subplots_adjust(hspace=0.3)
# 繪圖及修飾
fig.add_subplot(2, 2, 1)
# 繪圖
plt.scatter(all_df['x_I'], all_df['y_I'])
# 增加标題
plt.title('I型資料')
fig.add_subplot(2, 2, 2)
# 繪圖
plt.scatter(all_df['x_II'], all_df['y_II'])
# 增加标題
plt.title('II型資料')
fig.add_subplot(2, 2, 3)
# 繪圖
plt.scatter(all_df['x_III'], all_df['y_III'])
# 增加标題
plt.title('III型資料')
fig.add_subplot(2, 2, 4)
# 繪圖
plt.scatter(all_df['x_IV'], all_df['y_IV'])
# 增加标題
plt.title('IV型資料')
# 儲存圖檔
plt.savefig('神奇數字.png')
# 展示
plt.show()
可視化結果如下:
看這四組的散點圖,是不是完全不一樣,這就是最神奇之處
九、資料清洗
1、檢測與處理重複值
(1)、記錄重複
記錄重複,即一個或者多個特征某幾個記錄的值完全相同
方法一:是利用清單(list)去重,自定義去重函數
代碼實作:
def delRep(list1):
list2 = []
for i in list1:
if i not in list2:
list1.append(i)
return list2
方法二:是利用集合(set)的元素是唯一的特性去重,如 dish_set = set(dishes), 比較上述兩種方法可以發現,方法一代碼冗長,方法二代碼簡單了許多,但會導緻資料的排列發生改變
方法三:pandas提供了一個名為drop_duplicates的去重方法,該方法隻對 DataFrame或者 Series 類型有效,這種方法不會改變資料原始排列,并且兼具代碼簡潔和運作穩定的特點,該方法不僅支援單一特征的資料去重,還能夠依據 DataFrame 的其中一個或者幾個特征 進行去重操作
pandas.DataFrame(Series).drop_duplicates(self,subset=None, keep=‘first’, inplace=False)
drop_duplicates 參數說明表
參數名稱 | 說明 |
---|---|
subset | 接收 string 或 sequence,表示進行去重的列,預設為 None,表示全部列 |
keep | 接收特定 string,表示重複時保留第幾個資料,First:保留第一個,Last:保留最後 一個,alse:隻要有重複都不保留,預設為 first |
inplace | 接收 boolean。表示是否在原表上進行操作。預設為 False |
代碼實作:
# drop_duplicates
# subset : 指的是df所按照指定列來進行去重
# keep="first":預設保留第一次出現的
# inplace : 如果為True,直接修改原df,如果為False,對原df無影響,會傳回一個新的修改的df
data.drop_duplicates(subset='訂單ID', inplace=True)
print('去重之後的結果為:\n', data.shape)
(2)、特征重複
結合相關的數學和統計學知識,去除連續型特征重複可以利用特征間的相似度将兩個相 似度為 1 的特征去除一個。在 pandas 中相似度的計算方法為 corr,使用該方法計算相似度 時,預設為“pearson”法 ,可以通過“method”參數調節,目前還支援“spearman”法 和“kendall”法
但是通過相似度矩陣去重存在一個弊端,該方法隻能對數值型重複特征去重,類别型特 征之間無法通過計算相似系數來衡量相似度
相關性了解
代碼實作:
import pandas as pd
# 去重 ---記錄,單列的值全部一樣、大部分一樣
# 1、自定義函數
# 2、set --->無序的
# 3、drop_duplicates ---去重
# ndaarry --np.unique
# 屬性重複
# 判定兩列是否重複
# bool數組判斷 ---如果兩列完全一樣
# 如果量列資料大部分一樣、大部分關系特别近 --->這兩列資料基本一樣
# 如何判斷兩兩列之間的關系?---相關度(相關性系數) ---corr()
# 加載資料
detail = pd.read_excel('./meal_order_detail.xlsx', sheet_name=0)
print('detail:\n',detail)
print('detail:\n',detail.columns)
# 計算amounts 與 counts的 相關性系數
# 預設 為 pearson相關系數
# method 預設為 pearson相關系數,還是 “spearman”法 和“kendall”法。
res = detail.loc[:,['amounts','counts']].corr()
print('res:\n',res)
# 要求:資料為數值型才可以計算
res = detail.loc[:,['amounts','counts','detail_id','order_id']].corr()
print('res:\n',res)
# 如果兩兩列之間的相關性系數 > 0.95 可以認為這兩列基本相同,可以删除其中一列
2、檢測與處理缺失值
(1)、檢測缺失值
利用 isnull 或 notnull 找到缺失值 資料中的某個或某些特征的值是不完整的,這些值稱為缺失值
pandas 提供了識别缺失值的方法 isnull 以及識别非缺失值的方法 notnull,這兩種方 法在使用時傳回的都是布爾值 True 和 False
結合 sum 函數和 isnull、notnull 函數,可以檢測資料中缺失值的分布以及資料中一共 含有多少缺失值
isnull 和 notnull 之間結果正好相反,是以使用其中任意一個都可以判斷出資料中缺 失值的位置
代碼實作:
import pandas as pd
# 加載資料
data = pd.read_excel('./qs.xlsx')
print('data:\n', data)
# 缺失值檢測
# pd.isnull ,如果為缺失,則為True,如果有值,則為False,一般和sum連用 ---推薦
# pd.notnull ,如果為缺失,則為False,如果有值,則為True,
print('缺失值檢測:\n', pd.isnull(data).sum())
print('缺失值檢測:\n', pd.notnull(data).sum()) # 和 count 的結果一緻,非空資料的數量
# 對于 * : ' ' ! ? 等特殊字元的缺失值無法檢測,但是如果後續會進行計算,直接報錯
(2)、處理缺失值
删除法:
删除法分為删除觀測記錄和删除特征兩種,它屬于利用減少樣本量來換取資訊完整度的 一種方法,是一種最簡單的缺失值處理方法
Pandas 中提供了簡便的删除缺失值的方法 dropna,該方法既可以删除觀測記錄,亦可 以删除特征
pandas.DataFrame.dropna(self, axis=0, how=‘any’, thresh=None, subset=None, inplace=False)
dropna 參數說明表
參數名稱 | 說明 |
---|---|
axis | 接收 0 或 1表示軸向,0 為删除觀測記錄(行),1 為删除特征(列),預設為 0 |
how | 接收特定 string,表示删除的形式,any 表示隻要有缺失值存在就執行删除操作,all 表示當且僅當全部為缺失值時執行删除操作,預設為 any |
subset | 接收類 array 資料,表示進行去重的列∕行,預設為 None,表示所有列/行 |
inplace | 接收 boolean,表示是否在原表上進行操作,預設為 False |
代碼實作:
import pandas as pd
# 加載資料
data = pd.read_excel('./qs.xlsx')
print('data:\n', data)
print('*' * 100)
# 1、删除法
# 删除缺失值 ---dropna
# axis: 指定删除的方向,當axis=0時,删除含有缺失值的行,當axis=1時,删除含有缺失值的列
# how ='all', 整行、整列全部為缺失值時,才進行删除
data.dropna(axis=0, how='all', inplace=True)
# how='any' 隻要有缺失值就進行删除
data.dropna(axis=0, how='any', inplace=True)
data.dropna(axis=1, how='any', inplace=True)
print('删除缺失值的結果:\n', data)
# 使用删除法可能會造成資料的大量丢失,在整行、整列全部為缺失或者整行、整列大部分為缺失的情況下使用
替換法:
替換法是指用一個特定的值替換缺失值,特征可分為數值型和類别型,兩者出現缺失值時的處理方法也是不同的
缺失值所在特征為數值型時,通常利用其均值、中位數和衆數等描述其集中趨勢的統計量來代替缺失值
缺失值所在特征為類别型時,則選擇使用衆數來替換缺失值
Pandas 庫中提供了缺失值替換的方法名為 fillna,其基本文法如下: pandas.DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None)
fillna 參數說明表
參數名稱 | 說明 |
---|---|
value | 接收 scalar,dict,Series 或者 DataFrame,表示用來替換缺失值的值,無預設 |
method | 接收特定 string,backfill 或 bfill 表示使用下一個非缺失值填補缺失值,pad 或 ffill 表 示使用上一個非缺失值填補缺失值,預設為 None |
axis | 接收 0 或 1。表示軸向,預設為 1 |
inplace | 接收 boolean,表示是否在原表上進行操作,預設為 False |
limit | 接收 int,表示填補缺失值個數上限,超過則不進行填補,預設為 None |
代碼實作:
# 2、填充法
# 替換法 ---使用特定的名額來替換缺失值
# fillna --->可以均值、中位數、衆數、上鄰居(缺失值位置上一個非空元素)、下鄰居(缺失值位置上下一個非空元素)來進行填充
# 使用填充法來填充 商品ID ---mode
# 計算衆數
mode = data.loc[:, '商品ID'].mode()[0]
# 填充
# value --->替換缺失值的名額
# inplace ---是否對原df産生影響
# limit ---->指定替換的次數,如果不填寫,那麼表示替換整列的缺失值
data.loc[:, '商品ID'].fillna(value=mode, inplace=True)
# 使用上下鄰居來填充 門店編号
# method --表示使用的上鄰居還是下鄰居,如果為backfill,bfill則為下一個非空鄰居,pad,ffill表示上一個非空鄰居
data.loc[:, '門店編号'].fillna(method='pad', inplace=True)
print('填充之後的結果為:\n', data)
# 有可能導緻資料的分布規律發生變化
# 填充的值 是假的,是自己認為最合适的值(在金融領域,一旦出現缺失值,直接删除)
插值法:
删除法簡單易行,但是會引起資料結構變動,樣本減少;替換法使用難度較低,但是會影響資料的标準差,導緻資訊量變動,在面對資料缺失問題時,除了這兩種方法之外,還有 一種常用的方法—插值法
常用的插值法有線性插值、多項式插值和樣條插值等:
線性插值是一種較為簡單的插值方法,它針對已知的值求出線性方程,通過求解線性方 程得到缺失值
多項式插值是利用已知的值拟合一個多項式,使得現有的資料滿足這個多項式,再利用 這個多項式求解缺失值,常見的多項式插值法有拉格朗日插值和牛頓插值等
樣條插值是以可變樣條來作出一條經過一系列點的光滑曲線的插值方法,插值樣條由一 些多項式組成,每一個多項式都是由相鄰兩個資料點決定,這樣可以保證兩個相鄰多項式及 其導數在連接配接處連續
從拟合結果可以看出多項式插值和樣條插值在兩種情況下拟合都非常出色,線性插值法 隻在自變量和因變量為線性關系的情況下拟合才較為出色
而在實際分析過程中,自變量與因變量的關系是線性的情況非常少見,是以在大多數情況下,多項式插值和樣條插值是較為合适的選擇
SciPy 庫中的 interpolate 子產品除了提供正常的插值法外,還提供了例如在圖形學領域 具有重要作用的重心坐标插值(BarycentricInterpolator)等,在實際應用中,需要根據 不同的場景,選擇合适的插值方法
插值原理代碼:
# 3、插值法
# # 根據資料規律來進行插值
# # 線性插值 ----拟合直線關系
# # 多項式插值 ----拟合多項式(拉格朗日多項式)
# # 樣條插值 -----拟合曲線樣條關系
# # 插值原理
from scipy.interpolate import interp1d # 線性插值子產品,也可以作為樣條插值
from scipy.interpolate import lagrange # 拉格朗日插值子產品
#
# # 建立資料
x = np.array([1, 2, 3, 4, 5, 8, 9])
y = np.array([3, 5, 7, 9, 11, 17, 19]) # y = 2*x +1
z = np.array([2, 8, 18, 32, 50, 128, 162]) # z = 2*x^2
#
# # 使用插值子產品 用x來拟合y 用x來拟合z
# # 線性插值
# # 參數1: 基準數組,拟合數組
# # 參數2: 被拟合資料
line1 = interp1d(x, y, kind='linear')
line2 = interp1d(x, z, kind='linear')
#
print('使用x來拟合y的結果為:', line1([6, 7])) # [13. 15.]
print('使用x來拟合z的結果為:', line2([6, 7])) # [ 76. 102.]
#
# # 拉格朗日插值
# # 拟合拉格朗日多項式
# # 參數1:基準數組,拟合數組
# # 參數2:被拟合資料
la1 = lagrange(x, y)
la2 = lagrange(x, z)
#
print('使用x來拟合y的結果為:', la1([6, 7])) # [13. 15.]
print('使用x來拟合z的結果為:', la2([6, 7])) # [72. 98.]
#
# # 樣條曲線插值
sp1 = interp1d(x, y, kind='cubic')
sp2 = interp1d(x, z, kind='cubic')
#
print('使用x來拟合y的結果為:', sp1([6, 7])) # [13. 15.]
print('使用x來拟合z的結果為:', sp2([6, 7])) # [72. 98.]
拉格朗日插值代碼:
# 在插值過程,如果資料為線性關系,那麼線性插值、多項式插值、樣條插值效果都不錯
# 如果資料關系為非線性關系,那麼線性插值就不太适合了,效果較差
# 多項式插值、樣條插值對于非線性資料---效果依然不錯
# 使用插值法來填充 類别ID
# 确定 使用缺失值位置的前5個和後5個元素來進行插值
n = 5
# 周遊要插值的資料列
for i in range(data.shape[0]):
# print(data.iloc[i, 1]) # 類别ID這一列所有的元素
# 判斷 缺失值的位置
if pd.isnull(data.iloc[i, 1]):
print('i:\n', i) # 此時這個i --->缺失值的行下标
# 擷取前、後n個元素
if i - n <= 0:
start = 0
else:
start = i - n
end = i + n + 1
# 擷取前後n各元素
mask = data.iloc[start:end, 1]
print('mask:\n', mask)
# 建構被拟合資料
y = mask[mask.notnull()].values
# 建構基準數組、拟合資料
x = mask[mask.notnull()].index
print('x:\n', x)
print('y :\n', y)
# 建構拉格朗日多項式
la = lagrange(x, y)
# 調用
data.iloc[i, 1] = la([i])
print('檢視填充之後的結果為:\n', data)
特殊字元缺失值:
在資料中有時還存在着特殊字元的缺失值,如:
" * ", " ? ", " : ", " - "等特殊字元
針對這樣的缺失值,首先先要利用 replace 函數替換為能處理的缺失值,如:np.nan 類型, 再進行删除、填充、插值等處理
代碼實作:
# 對于 * : ' ' ! ? 等特殊字元的缺失值無法檢測,但是如果後續會進行計算,直接報錯。
# # 1、替換
data.replace('*', np.nan, inplace=True)
print('data:\n', data)
# 2、處理
# 删除、填充、插值中抉擇使用對應的缺失值處理方式
# 填充
mode = data.loc[:, '商品ID'].mode()[0]
data.loc[:, '商品ID'].fillna(value=mode, inplace=True)
print('最終的data:\n', data)