這篇博客用一個pandas的DataFrame類型的數據爲例,字段名爲了不與任何第三方庫混淆,我們叫他 dataframe
這篇博客沒有長篇大論,就是希望能夠讓大家直接複製代碼,然後把dataframe變量改爲自己的dataframe變量後立竿見影得到預期結果。
博客大多數的用例dataframe,運行 dataframe.head() 可以看到類似這樣的樣子,它源於真實數據:
tbi_value | tsi | bci | bpi | bdi | bsi | mask | |
---|---|---|---|---|---|---|---|
0 | 871.38 | 806.73 | 1523 | 854 | 782 | 768 | 0 |
1 | 875.55 | 807.63 | 1516 | 852 | 787 | 788 | 0 |
2 | 874.53 | 817.04 | 1515 | 858 | 798 | 810 | 0 |
3 | 874.61 | 817.56 | 1506 | 873 | 812 | 841 | 1 |
4 | 870.45 | 817.39 | 1503 | 889 | 824 | 864 | 1 |
import numpy as np
import pandas as pd
目錄
1.1.2 數據的均值、標準差、分位點、最小值、最大值、方差
1.描述性統計
1.1 數據基本信息
1.1.1 數據每個維度在計算機中的存儲信息
dataframe.info()
可以看到下面這個輸出:
<class 'pandas.core.frame.DataFrame'> # 表示是DataFrame類
RangeIndex: 1647 entries, 0 to 1646 # 行的index的值域
Data columns (total 7 columns): # 一共有多少列
tbi_value 1647 non-null float64 # 下面是每一列的描述, 列名稱 | 多少個非空的值 | 值類型
tsi 1647 non-null float64
bci 1647 non-null int64
bpi 1647 non-null int64
bdi 1647 non-null int64
bsi 1647 non-null int64
mask 1647 non-null int32
dtypes: float64(2), int32(1), int64(4) # 這是統計每個類型的總個數
memory usage: 83.7 KB # 這是內存佔用,83.7k只佔了非常少
1.1.2 數據的均值、標準差、分位點、最小值、最大值、方差
dataframe.describe()
可以看到下面這個輸出:
tbi_value | tsi | bci | bpi | bdi | bsi | mask | |
count | 1647 | 1647 | 1647 | 1647 | 1647 | 1647 | 1647 |
mean | 720.862125 | 645.353297 | 1683.73224 | 1022.375228 | 1016.361263 | 850.625987 | 0.477231 |
std | 176.724807 | 129.814479 | 862.435869 | 373.262046 | 360.824353 | 220.219328 | 0.499633 |
min | 328.8 | 391.14 | 161 | 282 | 295 | 247 | 0 |
25% | 571.69 | 533.895 | 1104 | 727 | 751 | 694 | 0 |
50% | 758.21 | 640.19 | 1544 | 984 | 966 | 873 | 0 |
75% | 843.475 | 750.16 | 2198 | 1307 | 1204.5 | 989 | 1 |
max | 1251.13 | 979.47 | 4329 | 2096 | 2337 | 1562 | 1 |
其中 mean表示平均值,std爲標準差,25%,50%,75%都是四分位點的值。
1.1.3 線性相關係數(皮爾森相關係數)
dataframe.corr()
tbi_value | tsi | bci | bpi | bdi | bsi | mask | |
tbi_value | 1 | 0.852856 | 0.763435 | 0.736944 | 0.83917 | 0.841345 | -0.052892 |
tsi | 0.852856 | 1 | 0.473282 | 0.457007 | 0.535706 | 0.696586 | -0.060913 |
bci | 0.763435 | 0.473282 | 1 | 0.693459 | 0.91269 | 0.633547 | -0.022511 |
bpi | 0.736944 | 0.457007 | 0.693459 | 1 | 0.887674 | 0.820403 | -0.005235 |
bdi | 0.83917 | 0.535706 | 0.91269 | 0.887674 | 1 | 0.832272 | -0.019859 |
bsi | 0.841345 | 0.696586 | 0.633547 | 0.820403 | 0.832272 | 1 | -0.036575 |
mask | -0.052892 | -0.060913 | -0.022511 | -0.005235 | -0.019859 | -0.036575 | 1 |
max | 1251.13 | 979.47 | 4329 | 2096 | 2337 | 1562 | 1 |
值域爲 【-1,1】,越靠近-1則是負相關,越靠近1則是正相關,越靠近0則越無關;
1.1.4 協方差矩陣
dataframe.cov()
tbi_value | tsi | bci | bpi | bdi | bsi | mask | |
tbi_value | 31231.65755 | 19565.75481 | 116358.0163 | 48612.2877 | 53511.05415 | 32743.63278 | -4.670225 |
tsi | 19565.75481 | 16851.79894 | 52987.08238 | 22144.1779 | 25092.58877 | 19913.7606 | -3.950785 |
bci | 116358.0163 | 52987.08238 | 743795.6287 | 223234.4268 | 284017.9467 | 120326.3567 | -9.700207 |
bpi | 48612.2877 | 22144.1779 | 223234.4268 | 139324.5554 | 119553.7344 | 67436.73277 | -0.976263 |
bdi | 53511.05415 | 25092.58877 | 284017.9467 | 119553.7344 | 130194.2139 | 66132.73362 | -3.580166 |
bsi | 32743.63278 | 19913.7606 | 120326.3567 | 67436.73277 | 66132.73362 | 48496.55262 | -4.024317 |
mask | -4.670225 | -3.950785 | -9.700207 | -0.976263 | -3.580166 | -4.024317 | 0.249633 |
max | 1251.13 | 979.47 | 4329 | 2096 | 2337 | 1562 | 1 |
這裏協方差:大於0,就是正相關;小於0,就是負相關;等於0,就是完全無關;
絕對值越大,表示相關性的程度也越大(關聯性越強),財務管理中有句話叫“協方差越小風險越低”,就是意味着這個變量對大局影響很小。
1.1.5 方差、中位數、衆數
方差:
dataframe.var()
中位數:
dataframe.median()
衆數:
dataframe.mode()
tbi_value | tsi | bci | bpi | bdi | bsi | mask | |
0 | 574.29 | 742.4 | 1613 | 727 | 598 | 899 | 0 |
1 | NaN | NaN | 1935 | NaN | 1090 | 944 | NaN |
這樣的結果表示bci、bdi、bsi這兩個指標有2個衆數,而其他的都是1個衆數
1.1.6 查看一列中不同數值的個數
len(dataframe['列名'].unique())
這樣可以直接顯示dataframe這一列的不同種類的數量的個數,如果想要更詳細的信息,可以直接使用:
dataframe['列名'].unique()
1.2 數值計算
1.2.1 本列所有值累加、累乘
累加:
dataframe.cumsum()
tbi_value | tsi | bci | bpi | bdi | bsi | mask | |
0 | 871.38 | 806.73 | 1523 | 854 | 782 | 768 | 0 |
1 | 1746.93 | 1614.36 | 3039 | 1706 | 1569 | 1556 | 0 |
2 | 2621.46 | 2431.4 | 4554 | 2564 | 2367 | 2366 | 0 |
3 | 3496.07 | 3248.96 | 6060 | 3437 | 3179 | 3207 | 1 |
4 | 4366.52 | 4066.35 | 7563 | 4326 | 4003 | 4071 | 2 |
... | .... | ... | ... | ... | ... | ... | ... |
1642 | 1184476.07 | 1060500.7 | 2771523 | 1680054 | 1671110 | 1397796 | 783 |
1643 | 1185177.57 | 1061101.91 | 2771999 | 1680959 | 1671831 | 1398583 | 784 |
1644 | 1185871.16 | 1061700.62 | 2772420 | 1681881 | 1672543 | 1399372 | 785 |
1645 | 1186563.86 | 1062298.68 | 2772819 | 1682843 | 1673252 | 1400168 | 785 |
1646 | 1187259.92 | 1062896.88 | 2773107 | 1683852 | 1673947 | 1400981 | 786 |
看輸出的信息,大家可以看到這是一層層累加下去,第n行的值就是原始數據 第 n + (n-1) + (n-2) + ... + 1 行的值的總和。
累乘:
dataframe.cumprod()
與累加的輸出類似,但累乘數值容易爆表,最後會輸出 inf 表示已超出數據存儲範圍。
2. 數據清洗
2.1 缺失值處理
可以通過下面的代碼得到缺失值的數量:
dataframe.isnull().sum()
也可通過簡單的 .info() 來看缺失值的情況;
下面的代碼可以得到 dataframe的缺失值佔比情況:爲0就表示沒有缺失值
dataframe.isnull().sum()/len(dataframe)
缺失值在進行求和時,會被默認視爲0
2.1.1 確定值填充
使用 0 填充缺失值:
dataframe.fillna(0,inplace=True)
也經常用這一列的平均值填充:
dataframe.fillna(dataframe.mean(),inplace=True)
2.1.2 參考當前列其他值填充
dataframe.fillna(method='pad',inplace=True) #參考前面值
dataframe.fillna(method='bfill',inplace=True) #參考後面值
比如dataframe矩陣長這個樣子:
0 | 1 | 2 | |
0 | 1 | NaN | 2 |
1 | 9 | NaN | NaN |
2 | 3 | 4 | NaN |
3 | 5 | 6 | 7 |
如果使用 dataframe.fillna(method='pad') 就可以得到:可以看到每列的缺失值都根據前面出現的值進行填充
0 | 1 | 2 | |
0 | 1 | NaN | 2 |
1 | 9 | NaN | 2 |
2 | 3 | 4 | 2 |
3 | 5 | 6 | 7 |
如果使用 dataframe.fillna(method='bfill') 就可以得到:同理,每列缺失值都根據它之後最先出現的值填充
0 | 1 | 2 | |
0 | 1 | 4 | 2 |
1 | 9 | 4 | 7 |
2 | 3 | 4 | 7 |
3 | 5 | 6 | 7 |
2.1.3 刪除行
dataframe.dropna(axis = 0,inplace=True)
這個可以直接刪除有缺失值的行。
如果把axis=1,則會刪除列,不建議這樣做,除非這個維度的缺失值非常嚴重。
如果希望整行都缺失才刪除,可以使用:
dataframe.dropna(axis=0, how='all', inplace=True)
2.1.4 拉格朗日插值法填充
使用拉格朗日插值法可以迅速填充缺失值,但是當連續缺失5個以上的數據,拉格朗日插值法會出現非常大的誤差:
def lagrange_fill(dataframe,colname,k=5):
def ployinterp_column(s, n, k=5):
y = s[list(range(n-k, n)) + list(range(n+1, n+1+k))] #取數
y = y[y.notnull()] #剔除空值
return lagrange(y.index, list(y))(n) #插值並返回插值結果
for i,index in enumerate(dataframe[colname][dataframe[colname].isnull()==True].index):
dataframe[colname][index] = ployinterp_column(dataframe[colname],i)# todo 返回當前數據的位置
return dataframe
調用方法是:
df = lagrange_fill(df,'KMI')
# df爲dataframe格式的數據,'KMI'爲有缺失值的一列的名稱
2.2 異常值處理
當出現明顯不合理的值時,需要剔除掉這些異常值
2.2.1 根據確定條件篩選數據
dataframe= dataframe[ (dataframe['列名1'] < 800) & (dataframe['列名2'] > 600)]
上面的例子篩選出了 dataframe中 列名1 指標 <800 且 列名2 指標 >600 的數據
這個套路可以根據確定的條件無限篩選出想要的數據,注意每個獨立的小條件都要有括號 '( )'
2.2.2 根據正態分佈3∂原則異常值檢測
如果dataframe的某一列數據應該是呈現正態分佈的,那麼可以有如下篩選方案:
#.quantile(threshold)方法可以通過假定源數據服從正態分佈,然後計算位於95%的點的值
#當 threshold = .95時:
# 95.449974的數據在平均數左右兩個標準差的範圍內
# 99.730020%的數據在平均數左右三個標準差的範圍內
# 99.993666的數據在平均數左右三個標準差的範圍內
def std_delete(dataframe,colname,threshold=.95):
se = dataframe[colname]
return dataframe[se < se.quantile(threshold)]
#剔除掉指定列名中存在異常值的那一行
dataframe = std_delete(dataframe , colname='列名')
如果threshold = .95篩選條件感覺不太合適,也可以使 threshold = .97 或是 .99 ,但如果.99還不行,那麼就不能主觀的認爲是正態分佈,應該做假設檢驗了,看看是不是其他分佈。
2.2.3 Z-score 異常值檢測
zscore和3∂原則的計算思路相同,計算公式是:
其中 xi 是一個數據點,μ 是所有點 xi 的平均值,δ 是所有點 xi 的標準偏差。
def zscore_check(dataframe,colname,threshold=3):
se = dataframe[colname]
zscore = (se - se.mean()) / (se.std())
return dataframe[zscore.abs() < threshold]
dataframe = zscore_check(dataframe,'列名')
threshold 一般爲 2.5 ,3.0 ,3.5
2.2.4 基於MAD的Z-score 異常值檢測
MAD爲(Mean Absolute Deviation,中位數絕對偏差),是單變量數據集中樣本差異性的統計量,比標準差更有彈性,它的計算公式是:
在維基百科中有細緻的推理過程:https://en.wikipedia.org/wiki/Median_absolute_deviation
根據推理,我們得到一個結果:MAD 約等於 0.6745*δ ,這個結論有利於編程,因此:
def zscore_mad_check(dataframe,colname,threshold=3.5):
se = dataframe[colname]
MAD = (se - se.median()).abs().median()
zscore = ((se - se.median())* 0.6475 /MAD).abs()
return dataframe[zscore < threshold]
dataframe= zscore_mad_check(dataframe,'列名')
同樣的, threshold 一般設置爲 2.5 3.0 3.5
2.2.5 數據傾斜處理(偏度)
使用下面的代碼確認數據是否傾斜:
from scipy import stats
stats.mstats.skew(dataframe['列名']).data
如果值大於1,則證明存在傾斜;值越接近於0,越趨近於平緩,如果傾斜,則使用下面的代碼處理:
dataframe['列名'] = np.log(dataframe['列名'])
也可以使用這段代碼得到偏度大於1的列名,表示這些列需要額外注意:
def check_skew(dataframe):
skew_attention = []
for column in dataframe.columns:
if (dataframe[column].dtype == 'int64') or (dataframe[column].dtype == 'float64'):
skew = stats.mstats.skew(dataframe[column]).data
if skew >= 1:
skew_attention.append(column)
return skew_attention
2.3 非數值類型處理
2.3.1 字符特徵離散化(one-hot編碼)
比如dataframe是這個樣子:
fc1 | fc2 | fc3 | |
0 | 1 | a | 2 |
1 | 9 | None | NaN |
2 | 3 | b | 2 |
3 | 5 | a | 7 |
4 | 5 | c | 7 |
現在 fc2 需要整理一下(離散化):
dataframe= pd.get_dummies(dataframe,dummy_na=True,columns=['fc2'])
fc1 | fc3 | fc2_a | fc2_b | fc2_c | fc2_nan | |
0 | 1 | 2 | 1 | 0 | 0 | 0 |
1 | 9 | NaN | 0 | 0 | 0 | 1 |
2 | 3 | 2 | 0 | 1 | 0 | 0 |
3 | 5 | 7 | 1 | 0 | 0 | 0 |
4 | 5 | 7 | 0 | 0 | 1 | 0 |
原本是object類型,這樣就可以很快的變成01類型加以區分,常用於有固定選項的特徵中。
如果沒有 “dummy_na = True”,dataframe中就不會有 “fc2_nan” 這一列;其他不變
也可以指定離散某一個數值型的特徵:
# temp可以得到離散化 ‘fc1’ 這個特徵的 dataframe
temp = pd.get_dummies(dataframe['fc1'],dummy_na=True)
# 把 dataframe與 temp 拼接起來並且刪除已經被離散化的 ‘fc1’ 特徵
dataframe= dataframe.join(temp).drop('fc1',axis = 1)
可以得到把 fc1 離散化的結果:
fc2 | fc3 | fc1_1.0 | fc1_3.0 | fc1_5.0 | fc1_9.0 | fc1_nan | |
0 | a | 2 | 1 | 0 | 0 | 0 | 0 |
1 | None | NaN | 0 | 0 | 0 | 1 | 0 |
2 | b | 2 | 0 | 1 | 0 | 0 | 0 |
3 | a | 7 | 0 | 0 | 1 | 0 | 0 |
4 | c | 7 | 0 | 0 | 1 | 0 |
2.4 時間序列
2.4.1 數據重採樣
降採樣:將時間線壓縮
series.resample('M').sum()
這裏'M'代表將時間變爲以月份來記,之後的 .sum() 是對合並的數據的操作,也可以改爲 .mean() 求均值。
升採樣:將時間線拉長
series.resample('D').sum()
如果直接拉伸,會有很多NaN,因此升採樣一般情況下需要考慮空值的填充
空值取前面的值:
series.resample('D').ffill()
空值取後面的值:
series.resample('D').bfill()
線性填充:
ts.resample('H').interpolate()
參考鏈接(感謝)
pandas api:https://pandas.pydata.org/pandas-docs/stable/reference/index.html
sklearn api:https://scikit-learn.org/stable/modules/classes.html
Python數據分析之pandas統計分析:https://blog.csdn.net/A632189007/article/details/76176985
數據預處理與特徵選擇:https://blog.csdn.net/u010089444/article/details/70053104
總結:數據清洗的一些總結:https://blog.csdn.net/MrLevo520/article/details/77573757
異常值檢測方法彙總:https://segmentfault.com/a/1190000015926584
用Python做單變量數據集的異常點分析:https://my.oschina.net/taogang/blog/279402
Minitab 18 支持:https://support.minitab.com/zh-cn/minitab/18/
如果有 錯誤 or 補充 or 代碼解釋 or 其他需求,請留言;