from pandas import Series, DataFrame
import pandas as pd
參考:
https://amaozhao.gitbooks.io/pandas-notebook/content/
https://github.com/justmarkham/pandas-videos/blob/master/pandas.ipynb
創(chuàng)建series和dataframe
1.創(chuàng)建series
通過列表創(chuàng)建
obj = Series([4, 7, -5, 3])
obj2 = Series([4, 7, -5, 3], index=['d','b','a','c']) #指定索引
通過字典創(chuàng)建
sdata = {'Ohio':35000, 'Texas':7100, 'Oregon':1600,'Utah':500}
obj3 = Series(sdata)
通過字典加索引
states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4 = Series(sdata, index=states)
2.創(chuàng)建dataframe
詞典生成
data = {'state':['Ohio', 'Ohio', 'Ohio', 'Nevada','Nevada'],
'year':[2000, 2001, 2002, 2011, 2002],
'pop':[1.5, 1.7, 3.6, 2.4, 2.9]}
frame = DataFrame(data)
frame2 = DataFrame(data, columns=['year', 'state', 'pop']) #指定列
frame3 = DataFrame(data, columns=['year', 'state', 'pop'],
index=['one', 'two', 'three', 'four', 'five']) #指定列和索引
列表生成(常用)
errors = [('c',1,'right'), ('b', 2,'wrong')]
df = pd.DataFrame(errors沟涨,columns=['name', 'count', 'result'])
嵌套詞典
pop = {'Nevada':{2001:2.4, 2002:2.9},
'Ohio':{2000:1.5, 2001:1.7, 2002:3.6}}
frame4 = DataFrame(pop)
Out[138]:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
series組合
In [4]: a = pd.Series([1,2,3])
In [5]: b = pd.Series([2,3,4])
In [6]: c = pd.DataFrame([a,b])
In [7]: c
Out[7]:
0 1 2
0 1 2 3
1 2 3 4
#或者也可以按照列來生成dataframe
c = pd.DataFrame({'a':a,'b':b})
索引和選取
對于一組數(shù)據(jù):
data = DataFrame(np.arange(16).reshape((4,4)),index=['Ohio', 'Colorado','Utah','New York'],columns=['one','two','three','four'])
>>> data
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
幾種索引方式
事實(shí)上有很多種索引方式峻呕,ix是較老的方式
data['two']
data.ix['Ohio']
data.ix['Ohio', ['two','three']]
較新的是loc與iloc
loc用于索引中有文字的,iloc僅僅用于索引為數(shù)字的
然后比較坑的是經(jīng)常是索引不成功氮唯,
對于列來說:這時候要檢查下這個名字對不對准脂?可以用
data.columns=[]
來強(qiáng)行規(guī)定名字然后再索引
對于行來說:可以強(qiáng)行reset_index或者set_index為新的一組值,基本可以避免大坑
按照索引來遍歷
按照行來進(jìn)行勤讽,這個操作其實(shí)很低效率
lbsdata=pd.read_csv('testlocation.csv',sep='\t')
lbstagRow = pd.DataFrame()
lbstag = pd.DataFrame()
for index, row in lbsdata.iterrows():
lbstagRow['recommend'] = [jsonToSp['formatted_address']]
lbstagRow['city'] = [jsonToSparse['addressComponent']]
lbstag = lbstag.append(lbstagRow,ignore_index=True)
也可以:
for ix, row in df.iterrows():
for ix, col in df.iteritems():
for i in range(0,len(df)):
排序
按列排序
In[73]: obj = Series(range(4), index=['d','a','b','c'])
In[74]: obj.sort_index()
Out[74]:
a 1
b 2
c 3
d 0
dtype: int64
In[78]: frame = DataFrame(np.arange(8).reshape((2,4)),index=['three', 'one'],columns=['d','a','b','c'])
In[79]: frame
Out[79]:
d a b c
three 0 1 2 3
one 4 5 6 7
In[86]: frame.sort_index()
Out[86]:
d a b c
one 4 5 6 7
three 0 1 2 3
In[87]: frame.sort()
Out[87]:
d a b c
one 4 5 6 7
three 0 1 2 3
按行排序
In[89]: frame.sort_index(axis=1, ascending=False)
Out[89]:
d c b a
three 0 3 2 1
one 4 7 6 5
按值排序
frame.sort_values('a')
刪除
刪除指定軸上的項
即刪除 Series 的元素或 DataFrame 的某一行(列)的意思厢漩,通過對象的 .drop(labels, axis=0) 方法:
In[11]: ser = Series([4.5,7.2,-5.3,3.6], index=['d','b','a','c'])
In[13]: ser.drop('c')
Out[13]:
d 4.5
b 7.2
a -5.3
dtype: float64
刪除行和列
In[17]: df = DataFrame(np.arange(9).reshape(3,3), index=['a','c','d'], columns=['oh','te','ca'])
In[18]: df
Out[18]:
oh te ca
a 0 1 2
c 3 4 5
d 6 7 8
In[19]: df.drop('a')
Out[19]:
oh te ca
c 3 4 5
d 6 7 8
In[20]: df.drop(['oh','te'],axis=1)
Out[20]:
ca
a 2
c 5
d 8
刪除點(diǎn)
df_train.sort_values(by = 'GrLivArea',ascending = False)[:2]
df_train = df_train.drop(df_train[df_train['Id'] == 1299].index)
df_train = df_train.drop(df_train[df_train['Id'] == 524].index)
DataFrame連接
算術(shù)運(yùn)算
In[5]: df1 = DataFrame(np.arange(12.).reshape((3,4)),columns=list('abcd'))
In[6]: df2 = DataFrame(np.arange(20.).reshape((4,5)),columns=list('abcde'))
In[9]: df1+df2
Out[9]:
a b c d e
0 0 2 4 6 NaN
1 9 11 13 15 NaN
2 18 20 22 24 NaN
3 NaN NaN NaN NaN NaN
# 傳入填充值
In[11]: df1.add(df2, fill_value=0)
Out[11]:
a b c d e
0 0 2 4 6 4
1 9 11 13 15 9
2 18 20 22 24 14
3 15 16 17 18 19
merge
pandas.merge可根據(jù)一個或多個鍵將不同DataFrame中的行連接起來膜眠。
默認(rèn)情況下,merge做的是“inner”連接溜嗜,結(jié)果中的鍵是交集宵膨,其它方式還有“l(fā)eft”,“right”炸宵,“outer”辟躏。“outer”外連接求取的是鍵的并集土全,組合了左連接和右連接捎琐。
In[14]: df1 = DataFrame({'key':['b','b','a','c','a','a','b'],'data1':range(7)})
In[15]: df2 = DataFrame({'key':['a','b','d'],'data2':range(3)})
In[18]: pd.merge(df1, df2) #或顯式: pd.merge(df1, df2, on='key')
Out[18]:
data1 key data2
0 0 b 1
1 1 b 1
2 6 b 1
3 2 a 0
4 4 a 0
5 5 a 0
concat
這種數(shù)據(jù)合并運(yùn)算被稱為連接(concatenation)、綁定(binding)或堆疊(stacking)裹匙。默認(rèn)情況下瑞凑,concat是在axis=0(行)上工作的,最終產(chǎn)生一個新的Series概页。如果傳入axis=1(列)籽御,則變成一個DataFrame。
pd.concat([s1,s2,s3], axis=1)
dfs = []
for classify in classify_finance + classify_other:
sql = "select classify, tags from {} where classify='{}' length(tags)>0 limit 1000".format(mysql_table_sina_news_all, classify)
df = pd.read_sql(sql,engine)
dfs.append(df)
df_all = pd.concat(dfs, ignore_index=True)
數(shù)據(jù)轉(zhuǎn)換
數(shù)據(jù)過濾、清理以及其他的轉(zhuǎn)換工作技掏。
移除重復(fù)數(shù)據(jù)(去重)
duplicated()
DataFrame的duplicated方法返回一個布爾型Series铃将,表示各行是否是重復(fù)行
df = DataFrame({'k1':['one']*3 + ['two']*4, 'k2':[1,1,2,3,3,4,4]})
df.duplicated()
df.drop_duplicates()
利用函數(shù)或映射進(jìn)行數(shù)據(jù)轉(zhuǎn)換
對于數(shù)據(jù):
df = DataFrame({'food':['bacon','pulled pork','bacon','Pastraml','corned beef', 'Bacon', 'pastraml','honey ham','nova lox'],'ounces':[4,3,12,6,7.5,8,3,5,6]})
增加一列表示該肉類食物來源的動物類型,先編寫一個肉類到動物的映射:
meat_to_animal = {'bacon':'pig',
'pulled pork':'pig',
'pastraml':'cow',
'corned beef':'cow',
'honey ham':'pig',
'nova lox':'salmon'}
map
Series的map方法可以接受一個函數(shù)或含有映射關(guān)系的字典型對象哑梳。
df['animal'] = df['food'].map(str.lower).map(meat_to_animal)
df['food'].map(lambda x : meat_to_animal[x.lower()])
Out[22]:
0 pig
1 pig
2 pig
3 cow
4 cow
5 pig
6 cow
7 pig
8 salmon
apply 和 applymap
對于DataFrame:
In[21]: df = DataFrame(np.random.randn(4,3), columns=list('bde'),index=['Utah','Ohio','Texas','Oregon'])
In[22]: df
Out[22]:
b d e
Utah 1.654850 0.594738 -1.969539
Ohio 2.178748 1.127218 0.451690
Texas 1.209098 -0.604432 -1.178433
Oregon 0.286382 0.042102 -0.345722
apply將函數(shù)應(yīng)用到由各列或行所形成的一維數(shù)組上劲阎。
作用到列:
In[24]: f = lambda x : x.max() - x.min()
In[25]: df.apply(f)
Out[25]:
b 1.892366
d 1.731650
e 2.421229
dtype: float64
作用到行
In[26]: df.apply(f, axis=1)
Out[26]:
Utah 3.624390
Ohio 1.727058
Texas 2.387531
Oregon 0.632104
dtype: float64
作用到每個元素
In[70]: frame = DataFrame(np.random.randn(4,3), columns=list('bde'),index=['Utah','Ohio','Texas','Oregon'])
In[72]: frame.applymap(lambda x : '%.2f' % x)
Out[72]:
b d e
Utah 1.19 1.56 -1.13
Ohio 0.10 -1.03 -0.04
Texas -0.22 0.77 -0.73
Oregon 0.22 -2.06 -1.25
numpy的ufuncs
Numpy的ufuncs(元素級數(shù)組方法)也可用于操作pandas對象。
取絕對值操作np.abs(),np.max(),np.min()
替換值
In[23]: se = Series([1, -999, 2, -999, -1000, 3])
In[24]: se.replace(-999, np.nan)
Out[24]:
0 1
1 NaN
2 2
3 NaN
4 -1000
5 3
dtype: float64
In[25]: se.replace([-999, -1000], np.nan)
Out[25]:
0 1
1 NaN
2 2
3 NaN
4 NaN
5 3
dtype: float64
In[26]: se.replace([-999, -1000], [np.nan, 0])
Out[26]:
0 1
1 NaN
2 2
3 NaN
4 0
5 3
dtype: float64
# 字典
In[27]: se.replace({-999:np.nan, -1000:0})
Out[27]:
0 1
1 NaN
2 2
3 NaN
4 0
5 3
dtype: float64
df.rename(index=str.title, columns=str.upper)
df.rename(index={'OHIO':'INDIANA'}, columns={'three':'peekaboo'})
離散化和面元劃分
pd.cut
為了便于分析鸠真,連續(xù)數(shù)據(jù)常常離散化或拆分為“面元”(bin)悯仙。比如:
ages = [20, 22,25,27,21,23,37,31,61,45,41,32]
需要將其劃分為“18到25”, “26到35”,“36到60”以及“60以上”幾個面元。要實(shí)現(xiàn)該功能,需要使用pandas的cut函數(shù)坷襟。
bins = [18, 25, 35, 60, 100]
cats = pd.cut(ages, bins)
Out[39]:
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
Length: 12
Categories (4, object): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
可以通過right=False指定哪端是開區(qū)間或閉區(qū)間裁眯。也可以指定面元的名稱:
cats = pd.cut(ages, bins, right=False)
group_name = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
cats = pd.cut(ages, bins, labels=group_name)
pd.value_counts(cats)
Out[46]:
Youth 5
MiddleAged 3
YoungAdult 3
Senior 1
dtype: int64
pd.qcut
qcut是一個非常類似cut的函數(shù),它可以根據(jù)樣本分位數(shù)對數(shù)據(jù)進(jìn)行面元劃分蠢终,根據(jù)數(shù)據(jù)的分布情況序攘,cut可能無法使各個面元中含有相同數(shù)量的數(shù)據(jù)點(diǎn),而qcut由于使用的是樣本分位數(shù)寻拂,可以得到大小基本相等的面元程奠。
In[48]: data = np.random.randn(1000)
In[49]: cats = pd.qcut(data, 4)
In[50]: cats
Out[50]:
[(0.577, 3.564], (-0.729, -0.0341], (-0.729, -0.0341], (0.577, 3.564], (0.577, 3.564], ..., [-3.0316, -0.729], [-3.0316, -0.729], (-0.0341, 0.577], [-3.0316, -0.729], (-0.0341, 0.577]]
Length: 1000
Categories (4, object): [[-3.0316, -0.729] < (-0.729, -0.0341] < (-0.0341, 0.577] < (0.577, 3.564]]
In[51]: pd.value_counts(cats)
Out[51]:
(0.577, 3.564] 250
(-0.0341, 0.577] 250
(-0.729, -0.0341] 250
[-3.0316, -0.729] 250
dtype: int64
檢測和過濾異常值
可以先describe然后在找出超過某一個閾值的
data[(np.abs(data)>3).any(1)]
data[np.abs(data)>3] = np.sign(data)*3
In[63]: data.describe()
Out[63]:
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.067623 0.068473 0.025153 -0.002081
std 0.995485 0.990253 1.003977 0.989736
min -3.000000 -3.000000 -3.000000 -3.000000
25% -0.774890 -0.591841 -0.641675 -0.644144
50% -0.116401 0.101143 0.002073 -0.013611
75% 0.616366 0.780282 0.680391 0.654328
max 3.000000 2.653656 3.000000 3.000000
單因素分析
這里的關(guān)鍵在于如何建立閾值,定義一個觀察值為異常值祭钉。我們對數(shù)據(jù)進(jìn)行正態(tài)化瞄沙,意味著把數(shù)據(jù)值轉(zhuǎn)換成均值為0,方差為1的數(shù)據(jù)慌核。
saleprice_scaled= StandardScaler().fit_transform(df_train['SalePrice'][:,np.newaxis]);
low_range = saleprice_scaled[saleprice_scaled[:,0].argsort()][:10]
high_range= saleprice_scaled[saleprice_scaled[:,0].argsort()][-10:]
print('outer range (low) of the distribution:')
print(low_range)
print('\nouter range (high) of thedistribution:')
print(high_range)
進(jìn)行正態(tài)化后距境,可以看出:
低范圍的值都比較相似并且在0附近分布。
高范圍的值離0很遠(yuǎn)垮卓,并且七點(diǎn)幾的值遠(yuǎn)在正常范圍之外垫桂。
雙變量分析
'GrLivArea'和'SalePrice'雙變量分析
var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
相關(guān)性探索
與數(shù)字型變量的關(guān)系
var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000));
與類別型變量的關(guān)系
var = 'OverallQual'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);
相關(guān)性分析
相關(guān)系數(shù)矩陣
corrmat = df_train.corr()
f, ax = plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=.8, square=True);
最近的相關(guān)系數(shù)矩陣
k = 10 #number ofvariables for heatmap
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(df_train[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={'size': 10},
yticklabels=cols.values, xticklabels=cols.values)
plt.show()
兩兩之間的散點(diǎn)圖
sns.set()
cols = ['SalePrice', 'OverallQual', 'GrLivArea','GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
sns.pairplot(df_train[cols], size = 2.5)
plt.show();
缺失數(shù)據(jù)
關(guān)于缺失數(shù)據(jù)需要思考的重要問題:
這一缺失數(shù)據(jù)的普遍性如何?
缺失數(shù)據(jù)是隨機(jī)的還是有律可循粟按?
這些問題的答案是很重要的诬滩,因為缺失數(shù)據(jù)意味著樣本大小的縮減,這會阻止我們的分析進(jìn)程灭将。除此之外疼鸟,以實(shí)質(zhì)性的角度來說,我們需要保證對缺失數(shù)據(jù)的處理不會出現(xiàn)偏離或隱藏任何難以忽視的真相庙曙。
total= df_train.isnull().sum().sort_values(ascending=False)
percent = (df_train.isnull().sum()/df_train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total','Percent'])
missing_data.head(20)
當(dāng)超過15%的數(shù)據(jù)都缺失的時候空镜,我們應(yīng)該刪掉相關(guān)變量且假設(shè)該變量并不存在。
根據(jù)這一條,一系列變量都應(yīng)該刪掉姑裂,例如'PoolQC', 'MiscFeature', 'Alley'等等馋袜,這些變量都不是很重要,因為他們基本都不是我們買房子時會考慮的因素舶斧。
df_train= df_train.drop((missing_data[missing_data['Total'] > 1]).index,1)
df_train= df_train.drop(df_train.loc[df_train['Electrical'].isnull()].index)
df_train.isnull().sum().max() #justchecking that there's no missing data missing..
多元分析
正態(tài)性:
應(yīng)主要關(guān)注以下兩點(diǎn):
直方圖 - 峰度和偏度欣鳖。
正態(tài)概率圖 - 數(shù)據(jù)分布應(yīng)緊密跟隨代表正態(tài)分布的對角線。
- ‘SalePrice’
繪制直方圖和正態(tài)概率圖:
sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)
可以看出茴厉,房價分布不是正態(tài)的泽台,顯示了峰值,正偏度矾缓,但是并不跟隨對角線怀酷。
可以用對數(shù)變換來解決這個問題
進(jìn)行對數(shù)變換:
df_train['SalePrice']= np.log(df_train['SalePrice'])
繪制變換后的直方圖和正態(tài)概率圖:
sns.distplot(df_train['SalePrice'], fit=norm);
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)
同方差性:
最好的測量兩個變量的同方差性的方法就是圖像。
- 'SalePrice' 和 'GrLivArea'同方差性
繪制散點(diǎn)圖:
plt.scatter(df_train['GrLivArea'],df_train['SalePrice']);
虛擬變量
將類別變量轉(zhuǎn)換為虛擬變量:
df_train = pd.get_dummies(df_train)
def factor(series):
levels = list(set(series))
design_matrix = np.zeros((len(series), len(levels)))
for row_index, elem in enumerate(series):
design_matrix[row_index, levels.index(elem)] = 1
name = series.name or ""
columns = map(lambda level: "%s[%s]" % (name, level), levels)
df = pd.DataFrame(design_matrix, index=series.index,
columns=columns)
return df
在處理類別變量的時候可以:
data.apply(lambda x: sum(x.isnull()))
var = ['Gender','Salary_Account','Mobile_Verified','Var1','Filled_Form','Device_Type','Var2','Source']
for v in var:
print '\nFrequency count for variable %s'%v
print data[v].value_counts()
print len(data['City'].unique())
#drop city because too many unique
data.drop('City',axis=1,inplace=True)
更加簡單的方法是:
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
var_to_encode = ['Device_Type','Filled_Form','Gender','Var1','Var2','Mobile_Verified','Source']
for col in var_to_encode:
data[col] = le.fit_transform(data[col])
data = pd.get_dummies(data, columns=var_to_encode)
data.columns
處理時間變量
data['Age'] = data['DOB'].apply(lambda x: 115 - int(x[-2:]))
data['Age'].head()
df_train.onlineDate=pd.to_datetime(df_train.onlineDate)
df_train['age']=(datetime.date.today()-df_train.onlineDate).dt.days
詞向量化
# import and init CountVectorizer
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
vectorizer # default parameters
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
lowercase=True, max_df=1.0, max_features=None, min_df=1,
ngram_range=(1, 1), preprocessor=None, stop_words=None,
strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
tokenizer=None, vocabulary=None)
# transform text into document-term matrix
X = vectorizer.fit_transform(reviews)
print(type(X)) # X is a sparse matrix
print(X)
處理百分號
p_float = df['p_str'].str.strip("%").astype(float)/100;
p_float_2 = p_float.round(decimals=2)
p_str_2 = p_float.apply(lambda x: format(x, '.2%'));
特征離散化
s_amount_C = df_lc_C['借款金額']
q = [0.05, 0.2, 0.5, 0.8, 0.95]
bins = s_amount_C.quantile(q)
s_fea_dct_amount_C = pd.cut(s_amount_C, add_ends_to_bins(bins.values))
dict_s_fea_dct['amount'] = s_fea_dct_amount_C
s_term_C = df_lc_C['借款期限']
q = [0.05, 0.2, 0.5, 0.8, 0.95]
bins = s_term_C.quantile(q)
sns.countplot(s_term_C);
看有多少個類別
def add_ends_to_bins(bins):
return np.append(np.insert(bins, 0, -np.inf), np.inf)
df_lc_C.groupby(s_type_C).size()
for col in category:
print(df_train2[str(col)].value_counts(dropna=False))
sns.countplot(s_is_first_C);
處理不同類別變量
DataFrame.convert_objects(convert_dates=True, convert_numeric=False, convert_timedeltas=True, copy=True)
這個命令非常好用嗜闻!
使效率倍增的Pandas使用技巧
本文取自Analytics Vidhya的一個帖子12 Useful Pandas Techniques in Python for Data Manipulation蜕依,瀏覽原帖可直接點(diǎn)擊鏈接,中文版可參見Datartisan的用 Python 做數(shù)據(jù)處理必看:12 個使效率倍增的 Pandas 技巧琉雳。這里主要對帖子內(nèi)容進(jìn)行檢驗并記錄有用的知識點(diǎn)样眠。
數(shù)據(jù)集
首先這個帖子用到的數(shù)據(jù)集是datahack的貸款預(yù)測(load prediction)競賽數(shù)據(jù)集,點(diǎn)擊鏈接可以訪問下載頁面翠肘,如果失效只需要注冊后搜索loan prediction競賽就可以找到了檐束。入坑的感興趣的同學(xué)可以前往下載數(shù)據(jù)集,我就不傳上來github了束倍。
先簡單看一看數(shù)據(jù)集結(jié)構(gòu)(此處表格可左右拖動):
Loan_ID | Gender | Married | Dependents | Education | Self_Employed | ApplicantIncome | CoapplicantIncome | LoanAmount | Loan_Amount_Term | Credit_History | Property_Area | Loan_Status |
---|---|---|---|---|---|---|---|---|---|---|---|---|
LP001002 | Male | No | 0 | Graduate | No | 5849 | 0 | 360 | 1 | Urban | Y | |
LP001003 | Male | Yes | 1 | Graduate | No | 4583 | 1508 | 128 | 360 | 1 | Rural | N |
LP001005 | Male | Yes | 0 | Graduate | Yes | 3000 | 0 | 66 | 360 | 1 | Urban | Y |
LP001006 | Male | Yes | 0 | Not Graduate | No | 2583 | 2358 | 120 | 360 | 1 | Urban | Y |
LP001008 | Male | No | 0 | Graduate | No | 6000 | 0 | 141 | 360 | 1 | Urban | Y |
共614條數(shù)據(jù)記錄被丧,每條記錄有十二個基本屬性,一個目標(biāo)屬性(Loan_Status)绪妹。數(shù)據(jù)記錄可能包含缺失值甥桂。
數(shù)據(jù)集很小,直接導(dǎo)入Python環(huán)境即可:
import pandas as pd
import numpy as np
data = pd.read_csv(r"F:\Datahack_Loan_Prediction\Loan_Prediction_Train.csv", index_col="Loan_ID")
注意這里我們指定index_col為Loan_ID這一列喂急,所以程序會把csv文件中Loan_ID這一列作為DataFrame的索引格嘁。默認(rèn)情況下index_col為False,程序自動創(chuàng)建int型索引廊移,從0開始糕簿。
1. 布爾索引(Boolean Indexing)
有時我們希望基于某些列的條件篩選出需要的記錄,這時可以使用loc索引的布爾索引功能狡孔。比方說下面的代碼篩選出全部無大學(xué)學(xué)歷但有貸款的女性列表:
data.loc[(data["Gender"]=="Female") & (data["Education"]=="Not Graduate") & (data["Loan_Status"]=="Y"), ["Gender","Education","Loan_Status"]]
loc索引是優(yōu)先采用label定位的懂诗,label可以理解為索引。前面我們定義了Loan_ID為索引苗膝,所以對這個DataFrame使用loc定位時就可以用Loan_ID定位:
data.loc['LP001002']
Gender Male
Married No
Dependents 0
Education Graduate
Self_Employed No
ApplicantIncome 5849
CoapplicantIncome 0
LoanAmount 129.937
Loan_Amount_Term 360
Credit_History 1
Property_Area Urban
Loan_Status Y
LoanAmount_Bin medium
Loan_Status_Coded 1
Name: LP001002, dtype: object
可以看到我們指定了一個Loan_ID殃恒,就定位到這個Loan_ID對應(yīng)的記錄。
loc允許四種input方式:
指定一個label;
label列表,比如
['LP001002','LP001003','LP001004']
;label切片离唐,比如
'LP001002':'LP001003'
;布爾數(shù)組
我們希望基于列值進(jìn)行篩選就用到了第4種input方式-布爾索引病附。使用()括起篩選條件,多個篩選條件之間使用邏輯運(yùn)算符&
,|
,~
與或非進(jìn)行連接亥鬓,特別注意完沪,和我們平常使用Python不同,這里用and
,or
,not
是行不通的嵌戈。
此外覆积,這四種input方式都支持第二個參數(shù),使用一個columns的列表熟呛,表示只取出記錄中的某些列宽档。上面的例子就是只取出了Gender
,Education
,Loan_Status
這三列,當(dāng)然庵朝,獲得的新DataFrame的索引依然是Loan_ID吗冤。
想了解更多請閱讀 Pandas Selecting and Indexing。
2. Apply函數(shù)
Apply是一個方便我們處理數(shù)據(jù)的函數(shù)九府,可以把我們指定的一個函數(shù)應(yīng)用到DataFrame的每一行或者每一列(使用參數(shù)axis設(shè)定欣孤,默認(rèn)為0,即應(yīng)用到每一列)昔逗。
如果要應(yīng)用到特定的行和列只需要先提取出來再apply就可以了,比如data['Gender'].apply(func)
篷朵。特別地勾怒,這里指定的函數(shù)可以是系統(tǒng)自帶的,也可以是我們定義的(可以用匿名函數(shù))声旺。比如下面這個例子統(tǒng)計每一列的缺失值個數(shù):
# 創(chuàng)建一個新函數(shù):
def num_missing(x):
return sum(x.isnull())
# Apply到每一列:
print("Missing values per column:")
print(data.apply(num_missing, axis=0)) # axis=0代表函數(shù)應(yīng)用于每一列
Missing values per column:
Gender 13
Married 3
Dependents 15
Education 0
Self_Employed 32
ApplicantIncome 0
CoapplicantIncome 0
LoanAmount 22
Loan_Amount_Term 14
Credit_History 50
Property_Area 0
Loan_Status 0
dtype: int64
下面這個例子統(tǒng)計每一行的缺失值個數(shù)笔链,因為行數(shù)太多,所以使用head函數(shù)僅打印出DataFrame的前5行:
# Apply到每一行:
print("\nMissing values per row:")
print(data.apply(num_missing, axis=1).head()) # axis=1代表函數(shù)應(yīng)用于每一行
Missing values per row:
Loan_ID
LP001002 1
LP001003 0
LP001005 0
LP001006 0
LP001008 0
dtype: int64
想了解更多請閱讀 Pandas Reference (apply)
3. 替換缺失值
一般來說我們會把某一列的缺失值替換為所在列的平均值/眾數(shù)/中位數(shù)腮猖。fillna()
函數(shù)可以幫我們實(shí)現(xiàn)這個功能鉴扫。但首先要從scipy庫導(dǎo)入獲取統(tǒng)計值的函數(shù)。
# 首先導(dǎo)入一個尋找眾數(shù)的函數(shù):
from scipy.stats import mode
GenderMode = mode(data['Gender'].dropna())
print(GenderMode)
print(GenderMode.mode[0],':',GenderMode.count[0])
ModeResult(mode=array(['Male'], dtype=object), count=array([489]))
Male : 489
F:\Anaconda3\lib\site-packages\scipy\stats\stats.py:257: RuntimeWarning: The input array could not be properly checked for nan values. nan values will be ignored.
"values. nan values will be ignored.", RuntimeWarning)
可以看到對DataFrame的某一列使用mode函數(shù)可以得到這一列的眾數(shù)以及它所出現(xiàn)的次數(shù)澈缺,由于眾數(shù)可能不止一個坪创,所以眾數(shù)的結(jié)果是一個列表,對應(yīng)地出現(xiàn)次數(shù)也是一個列表姐赡±吃ぃ可以使用.mode
和.count
提取出這兩個列表。
特別留意项滑,可能是版本原因依沮,我使用的scipy (0.17.0)不支持原帖子中的代碼,直接使用mode(data['Gender'])
是會報錯的:
F:\Anaconda3\lib\site-packages\scipy\stats\stats.py:257: RuntimeWarning: The input array could not be p
roperly checked for nan values. nan values will be ignored.
"values. nan values will be ignored.", RuntimeWarning)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "F:\Anaconda3\lib\site-packages\scipy\stats\stats.py", line 644, in mode
scores = np.unique(np.ravel(a)) # get ALL unique values
File "F:\Anaconda3\lib\site-packages\numpy\lib\arraysetops.py", line 198, in unique
ar.sort()
TypeError: unorderable types: str() > float()
必須使用mode(data['Gender'].dropna())
,傳入dropna()處理后的DataFrame才可以危喉。雖然Warning仍然存在宋渔,但是可以執(zhí)行得到結(jié)果。Stackoverflow里有這個問題的討論:Scipy Stats Mode function gives Type Error unorderable types辜限。指出scipy的mode函數(shù)無法處理列表中包含混合類型的情況皇拣,比方說上面的例子就是包含了缺失值NAN類型和字符串類型,所以無法直接處理列粪。
同時也指出Pandas自帶的mode函數(shù)是可以處理混合類型的审磁,我測試了一下:
from pandas import Series
Series.mode(data['Gender'])
0 Male
dtype: object
確實(shí)沒問題,不需要使用dropna()處理岂座,但是只能獲得眾數(shù)态蒂,沒有對應(yīng)的出現(xiàn)次數(shù)。返回結(jié)果是一個Pandas的Series對象费什〖鼗郑可以有多個眾數(shù),索引是int類型鸳址,從0開始瘩蚪。
掌握了獲取眾數(shù)的方法后就可以使用fiilna替換缺失值了:
#值替換:
data['Gender'].fillna(GenderMode.mode[0], inplace=True)
MarriedMode = mode(data['Married'].dropna())
data['Married'].fillna(MarriedMode.mode[0], inplace=True)
Self_EmployedMode = mode(data['Self_Employed'].dropna())
data['Self_Employed'].fillna(Self_EmployedMode.mode[0], inplace=True)
先提取出某一列,然后用fillna把這一列的缺失值都替換為計算好的平均值/眾數(shù)/中位數(shù)稿黍。inplace關(guān)鍵字用于指定是否直接對這個對象進(jìn)行修改疹瘦,默認(rèn)是False,如果指定為True則直接在對象上進(jìn)行修改巡球,其他地方調(diào)用這個對象時也會收到影響言沐。這里我們希望修改直接覆蓋缺失值,所以指定為True酣栈。
#再次檢查缺失值以確認(rèn):
print(data.apply(num_missing, axis=0))
Gender 0
Married 0
Dependents 15
Education 0
Self_Employed 0
ApplicantIncome 0
CoapplicantIncome 0
LoanAmount 22
Loan_Amount_Term 14
Credit_History 50
Property_Area 0
Loan_Status 0
dtype: int64
可以看到Gender
,Married
,Self_Employed
這幾列的缺失值已經(jīng)都替換成功了险胰,所以缺失值的個數(shù)為0。
想了解更多請閱讀 Pandas Reference (fillna)
4. 透視表
透視表(Pivot Table)是一種交互式的表矿筝,可以動態(tài)地改變它的版面布置起便,以便按照不同方式分析數(shù)據(jù),也可以重新安排行號窖维、列標(biāo)榆综、頁字段,比如下面的Excel透視表:
可以自由選擇用來做行號和列標(biāo)的屬性陈辱,非常便于我們做不同的分析奖年。Pandas也提供類似的透視表的功能。例如LoanAmount
這個重要的列有缺失值沛贪。我們不希望直接使用整體平均值來替換陋守,這樣太過籠統(tǒng)震贵,不合理。
這時可以用先根據(jù) Gender
水评、Married
猩系、Self_Employed
分組后,再按各組的均值來分別替換缺失值中燥。每個組 LoanAmount
的均值可以用如下方法確定:
#Determine pivot table
impute_grps = data.pivot_table(values=["LoanAmount"], index=["Gender","Married","Self_Employed"], aggfunc=np.mean)
print(impute_grps)
LoanAmount
Gender Married Self_Employed
Female No No 114.691176
Yes 125.800000
Yes No 134.222222
Yes 282.250000
Male No No 129.936937
Yes 180.588235
Yes No 153.882736
Yes 169.395833
關(guān)鍵字values
用于指定要使用集成函數(shù)(字段aggfunc
)計算的列寇甸,選填,不進(jìn)行指定時默認(rèn)對所有index以外符合集成函數(shù)處理類型的列進(jìn)行處理疗涉,比如這里使用mean
作集成函數(shù)拿霉,則合適的類型就是數(shù)值類型。index
是行標(biāo)咱扣,可以是多重索引绽淘,處理時會按序分組。
想了解更多請閱讀 Pandas Reference (Pivot Table)
5. 多重索引
不同于DataFrame對象闹伪,可以看到上一步得到的Pivot Table每個索引都是由三個值組合而成的沪铭,這就叫做多重索引。
從上一步中我們得到了每個分組的平均值偏瓤,接下來我們就可以用來替換LoanAmount
字段的缺失值了:
#只在帶有缺失值的行中迭代:
for i,row in data.loc[data['LoanAmount'].isnull(),:].iterrows():
ind = tuple([row['Gender'],row['Married'],row['Self_Employed']])
data.loc[i,'LoanAmount'] = impute_grps.loc[ind].values[0]
特別注意對Pivot Table使用loc定位的方式杀怠,Pivot Table的索引是一個tuple。從上面的代碼可以看到厅克,先把DataFrame中所有LoanAmount
字段缺失的數(shù)據(jù)記錄取出赔退,并且使用iterrows函數(shù)轉(zhuǎn)為一個按行迭代的對象,每次迭代返回一個索引(DataFrame里的索引)以及對應(yīng)行的內(nèi)容证舟。然后從行的內(nèi)容中把 Gender
离钝、Married
、Self_Employed
三個字段的值提取出放入一個tuple里褪储,這個tuple就可以用作前面定義的Pivot Table的索引了。
接下來的賦值對DataFrame使用loc定位根據(jù)索引定位慧域,并且只抽取LoanAmount
字段鲤竹,把它賦值為在Pivot Table中對應(yīng)分組的平均值。最后檢查一下昔榴,這樣我們又處理好一個列的缺失值了辛藻。
#再次檢查缺失值以確認(rèn):
print(data.apply(num_missing, axis=0))
Gender 0
Married 0
Dependents 15
Education 0
Self_Employed 0
ApplicantIncome 0
CoapplicantIncome 0
LoanAmount 0
Loan_Amount_Term 14
Credit_History 50
Property_Area 0
Loan_Status 0
dtype: int64
6. 二維表
二維表這個東西可以幫助我們快速驗證一些基本假設(shè),從而獲取對數(shù)據(jù)表格的初始印象互订。例如吱肌,本例中Credit_History
字段被認(rèn)為對會否獲得貸款有顯著影響⊙銮荩可以用下面這個二維表進(jìn)行驗證:
pd.crosstab(data["Credit_History"],data["Loan_Status"],margins=True)
crosstab
的第一個參數(shù)是index氮墨,用于行的分組纺蛆;第二個參數(shù)是columns,用于列的分組规揪。代碼中還用到了一個margins
參數(shù)桥氏,這個參數(shù)默認(rèn)為False,啟用后得到的二維表會包含匯總數(shù)據(jù)猛铅。
然而字支,相比起絕對數(shù)值,百分比更有助于快速了解數(shù)據(jù)奸忽。我們可以用apply函數(shù)達(dá)到目的:
def percConvert(ser):
return ser/float(ser[-1])
pd.crosstab(data["Credit_History"],data["Loan_Status"],margins=True).apply(percConvert, axis=1)
從這個二維表中我們可以看到有信用記錄 (Credit_History
字段為1.0) 的人獲得貸款的可能性更高堕伪。接近80%有信用記錄的人都 獲得了貸款,而沒有信用記錄的人只有大約8% 獲得了貸款栗菜。令人驚訝的是欠雌,如果我們直接利用信用記錄進(jìn)行訓(xùn)練集的預(yù)測,在614條記錄中我們能準(zhǔn)確預(yù)測出460條記錄 (不會獲得貸款+會獲得貸款:82+378) 苛萎,占總數(shù)足足75%桨昙。不過呢~在訓(xùn)練集上效果好并不代表在測試集上效果也一樣好。有時即使提高0.001%的準(zhǔn)確度也是相當(dāng)困難的腌歉,這就是我們?yōu)槭裁葱枰⒛P瓦M(jìn)行預(yù)測的原因了蛙酪。
想了解更多請閱讀 Pandas Reference (crosstab)
7. 數(shù)據(jù)框合并
就像數(shù)據(jù)庫有多個表的連接操作一樣,當(dāng)數(shù)據(jù)來源不同時翘盖,會產(chǎn)生把不同表格合并的需求桂塞。這里假設(shè)不同的房產(chǎn)類型有不同的房屋均價數(shù)據(jù),定義一個新的表格馍驯,如下:
prop_rates = pd.DataFrame([1000, 5000, 12000], index=['Rural','Semiurban','Urban'],columns=['rates'])
prop_rates
農(nóng)村房產(chǎn)均價只用1000阁危,城郊這要5000,城鎮(zhèn)內(nèi)房產(chǎn)比較貴汰瘫,均價為12000狂打。我們獲取到這個數(shù)據(jù)之后,希望把它連接到原始表格Loan_Prediction_Train.csv中以便觀察房屋均價對預(yù)測的影響混弥。在原始表格中有一列Property_Area就是表明貸款人居住的區(qū)域的趴乡,可以通過這一列進(jìn)行表格連接:
data_merged = data.merge(right=prop_rates, how='inner',left_on='Property_Area',right_index=True, sort=False)
data_merged.pivot_table(values='Credit_History',index=['Property_Area','rates'], aggfunc=len)
Property_Area rates
Rural 1000 179.0
Semiurban 5000 233.0
Urban 12000 202.0
Name: Credit_History, dtype: float64
使用merge函數(shù)進(jìn)行連接,解析一下各個參數(shù):
參數(shù)right即連接操作右端表格;
-
參數(shù)how指示連接方式蝗拿,默認(rèn)是inner晾捏,即內(nèi)連接哀托〉胄粒可選left、right仓手、outer胖齐、inner玻淑;
- left: use only keys from left frame (SQL: left outer join)
- right: use only keys from right frame (SQL: right outer join)
- outer: use union of keys from both frames (SQL: full outer join)
- inner: use intersection of keys from both frames (SQL: inner join)
參數(shù)left_on用于指定連接的key的列名,即使key在兩個表格中的列名不同市怎,也可以通過left_on和right_on參數(shù)分別指定岁忘。 如果一樣的話,使用on參數(shù)就可以了区匠「上瘢可以是一個標(biāo)簽(單列),也可以是一個列表(多列)驰弄;
right_index默認(rèn)為False麻汰,設(shè)置為True時會把連接操作右端表格的索引作為連接的key。同理還有l(wèi)eft_index戚篙;
sort參數(shù)默認(rèn)為False五鲫,指示是否需要按key排序。
所以上面的代碼是把data表格和prop_rates表格連接起來岔擂。連接時位喂,data表格用于連接的key是Property_Area
,而prop_rates表格用于連接的key是索引乱灵,它們的值域是相同的塑崖。
連接之后使用了第四小節(jié)透視表的方法檢驗新表格中Property_Area
字段和rates
字段的關(guān)系。后面跟著的數(shù)字表示出現(xiàn)的次數(shù)痛倚。
想了解更多請閱讀 Pandas Reference (merge)
8. 給數(shù)據(jù)排序
Pandas可以輕松地基于多列進(jìn)行排序规婆,方法如下:
data_sorted = data.sort_values(['ApplicantIncome','CoapplicantIncome'], ascending=False)
data_sorted[['ApplicantIncome','CoapplicantIncome']].head(10)
Notice:Pandas 的sort
函數(shù)現(xiàn)在已經(jīng)不推薦使用,使用 sort_values
函數(shù)代替蝉稳。
這里傳入了ApplicantIncome
和CoapplicantIncome
兩個字段用于排序抒蚜,Pandas會先按序進(jìn)行。先根據(jù)ApplicantIncome
進(jìn)行排序耘戚,對于ApplicantIncome
相同的記錄再根據(jù)CoapplicantIncome
進(jìn)行排序嗡髓。 ascending參數(shù)設(shè)為False,表示降序排列收津。不妨再看個簡單的例子:
from pandas import DataFrame
df_temp = DataFrame({'a':[1,2,3,4,3,2,1],'b':[0,1,1,0,1,0,1]})
df_sorted = df_temp.sort_values(['a','b'],ascending=False)
df_sorted
這里只有a和b兩列器贩,可以清晰地看到Pandas的多列排序是先按a列進(jìn)行排序,a列的值相同則會再按b列的值排序朋截。
想了解更多請閱讀 Pandas Reference (sort_values)
9. 繪圖(箱型圖&直方圖)
Pandas除了表格操作之外,還可以直接繪制箱型圖和直方圖且只需一行代碼吧黄。這樣就不必單獨(dú)調(diào)用matplotlib了部服。
%matplotlib inline
# coding:utf-8
data.boxplot(column="ApplicantIncome",by="Loan_Status")
箱型圖
因為之前沒怎么接觸過箱型圖,所以這里單獨(dú)開一節(jié)簡單歸納一下拗慨。
箱形圖(英文:Box-plot)廓八,又稱為盒須圖奉芦、盒式圖、盒狀圖或箱線圖剧蹂,是一種用作顯示一組數(shù)據(jù)分散情況資料的統(tǒng)計圖声功。因型狀如箱子而得名。詳細(xì)解析看維基百科宠叼。
因為上面那幅圖不太容易看先巴,用個簡單點(diǎn)的例子來說,還是上一小節(jié)那個只有a列和b列的表格冒冬。按b列對a列進(jìn)行分組:
df_temp.boxplot(column="a",by="b")
定義b列值為0的分組為Group1伸蚯,b列值為1的分組為Group2。Group1分組有4简烤,2剂邮,1三個值,毫無疑問最大值4横侦,最小值1挥萌,在箱型圖中這兩個值對應(yīng)箱子發(fā)出的虛線頂端的兩條實(shí)線。Group2分組有3枉侧,3引瀑,2,1四個值棵逊,由于最大值3和上四分位數(shù)3(箱子頂部)相同伤疙,所以重合了。
Group1中位數(shù)是2辆影,而Group2的中位數(shù)則是中間兩個數(shù)2和3的平均數(shù)徒像,也即2.5。在箱型圖中由箱子中間的有色線段表示蛙讥。
四分位數(shù)
四分位數(shù)是統(tǒng)計學(xué)的一個概念锯蛀。把所有數(shù)值由小到大排列好,然后分成四等份次慢,處于三個分割點(diǎn)位置的數(shù)值就是四分位數(shù)旁涤,其中:
- 第一四分位數(shù) (Q1),又稱“較小四分位數(shù)”或“下四分位數(shù)”迫像,等于該樣本中所有數(shù)值由小到大排列后劈愚,在四分之一位置的數(shù)。
- 第二四分位數(shù) (Q2)闻妓,又稱“中位數(shù)”菌羽,等于該樣本中所有數(shù)值由小到大排列后,在二分之一位置的數(shù)由缆。
- 第三四分位數(shù) (Q3)注祖,又稱“較大四分位數(shù)”或“上四分位數(shù)”猾蒂,等于該樣本中所有數(shù)值由小到大排列后,在四分之三位置的數(shù)是晨。
Notice:Q3與Q1的差距又稱四分位距(InterQuartile Range, IQR)肚菠。
計算四分位數(shù)時首先計算位置,假設(shè)有n個數(shù)字罩缴,則:
- Q1位置 = (n-1) / 4
- Q2位置 = 2 * (n-1) / 4 = (n-1) / 2
- Q3位置 = 3 * (n-1) / 4
如果n-1恰好是4的倍數(shù)蚊逢,那么數(shù)列中對應(yīng)位置的就是各個四分位數(shù)了。但是时捌,如果n-1不是4的倍數(shù)呢?
這時位置會是一個帶小數(shù)部分的數(shù)值炉抒,四分位數(shù)以距離該值最近的兩個位置的加權(quán)平均值求出奢讨。其中,距離較近的數(shù)焰薄,權(quán)值為小數(shù)部分拿诸;而距離較遠(yuǎn)的數(shù),權(quán)值為(1-小數(shù)部分)塞茅。
再看例子中的Group1亩码,Q1位置為0.5,Q2位置為1野瘦,Q3位置為1.5描沟。(注意:位置從下標(biāo)0開始!)鞭光,所以:
Q1 = 0.5*1+0.5*2 = 1.5
Q2 = 2
Q3 = 0.5*2+0.5*4 = 3
而Group2中吏廉,Q1位置為0.75,Q2位置為1.5惰许,Q3位置為2.25席覆。
Q1 = 0.25*1+0.72*2 = 1.75
Q2 = 0.5*2+0.5*3 = 2.5
Q3 = 0.25*3+0.75*3 = 3
這樣是否就清晰多了XD 然而,四分位數(shù)的取法還存在分歧汹买,定義不一佩伤,我在學(xué)習(xí)這篇文章時也曾經(jīng)很迷茫,直到閱讀了源碼;薇小生巡!
因為Pandas庫依賴numpy庫,所以它計算四分位數(shù)的方式自然也是使用了numpy庫的见妒。而numpy中實(shí)現(xiàn)計算百分比數(shù)的函數(shù)為percentile孤荣,代碼實(shí)現(xiàn)如下:
def percentile(N, percent, key=lambda x:x):
"""
Find the percentile of a list of values.
@parameter N - is a list of values. Note N MUST BE already sorted.
@parameter percent - a float value from 0.0 to 1.0.
@parameter key - optional key function to compute value from each element of N.
@return - the percentile of the values
"""
if not N:
return None
k = (len(N)-1) * percent
f = math.floor(k)
c = math.ceil(k)
if f == c:
return key(N[int(k)])
d0 = key(N[int(f)]) * (c-k)
d1 = key(N[int(c)]) * (k-f)
return d0+d1
讀一遍源碼之后就更加清晰了。最后舉個例子:
from numpy import percentile, mean, median
temp = [1,2,4] # 數(shù)列包含3個數(shù)
print(min(temp))
print(percentile(temp,25))
print(percentile(temp,50))
print(percentile(temp,75))
print(max(temp))
1
1.5
2.0
3.0
4
temp2 = [1,2,3,3] # 數(shù)列包含4個數(shù)
print(min(temp2))
print(percentile(temp2,25))
print(percentile(temp2,50))
print(percentile(temp2,75))
print(max(temp2))
1
1.75
2.5
3.0
3
temp3 = [1,2,3,4,5] # 數(shù)列包含5個數(shù)
print(min(temp2))
print(percentile(temp3,25))
print(percentile(temp3,50))
print(percentile(temp3,75))
print(max(temp3))
1
2.0
3.0
4.0
5
不熟悉的話就再手?jǐn)]一遍!@贰!不要怕麻煩7稻础K熳!這一小節(jié)到此Over~
直方圖
data.hist(column="ApplicantIncome",by="Loan_Status",bins=30)
直方圖比箱型圖熟悉一些劲赠,這里就不詳細(xì)展開了涛目。結(jié)合箱型圖和直方圖,我們可以看出獲得貸款的人和未獲得貸款的人沒有明顯的收入差異凛澎,也即收入不是決定性因素霹肝。
離群點(diǎn)
特別地,從直方圖上我們可以看出這個數(shù)據(jù)集在收入字段上塑煎,比較集中于一個區(qū)間沫换,區(qū)間外部有些散落的點(diǎn),這些點(diǎn)我們稱為離群點(diǎn)(Outlier)最铁。前面將箱型圖時沒有細(xì)說讯赏,現(xiàn)在再回顧一下箱型圖:
可以看到除了箱子以及最大最小值之外,還有很多橫線冷尉,這些橫線其實(shí)就表示離群點(diǎn)漱挎。那么可能又會有新的疑問了?怎么會有離群點(diǎn)在最大最小值外面呢雀哨?這樣豈不是存在比最大值大磕谅,比最小值小的情況了嗎?
其實(shí)之前提到一下雾棺,有一個概念叫四分位距(IQR)膊夹,數(shù)值上等于Q3-Q1锻离,記作ΔQ横媚。定義:
- 最大值區(qū)間: Q3+1.5ΔQ
- 最小值區(qū)間: Q1-1.5ΔQ
也就是說最大值必須出現(xiàn)在這兩個區(qū)間內(nèi)秸苗,區(qū)間外的值被視為離群點(diǎn)骏令,并顯示在圖上看成。這樣做我們可以避免被過分偏離的數(shù)據(jù)點(diǎn)帶偏媳拴,更準(zhǔn)確地觀測到數(shù)據(jù)的真實(shí)狀況治力,或者說普遍狀況碱鳞。
想了解更多請閱讀 Pandas Reference (hist) | Pandas Reference (boxplot)
10. 用Cut函數(shù)分箱
有時把數(shù)值聚集在一起更有意義侵佃。例如麻昼,如果我們要為交通狀況(路上的汽車數(shù)量)根據(jù)時間(分鐘數(shù)據(jù))建模。具體的分鐘可能不重要馋辈,而時段如“上午”“下午”“傍晚”“夜間”“深夜”更有利于預(yù)測抚芦。如此建模更直觀,也能避免過度擬合。
這里我們定義一個簡單的叉抡、可復(fù)用的函數(shù)尔崔,輕松為任意變量分箱。
#分箱:
def binning(col, cut_points, labels=None):
#Define min and max values:
minval = col.min()
maxval = col.max()
#利用最大值和最小值創(chuàng)建分箱點(diǎn)的列表
break_points = [minval] + cut_points + [maxval]
#如果沒有標(biāo)簽褥民,則使用默認(rèn)標(biāo)簽0 ... (n-1)
if not labels:
labels = range(len(cut_points)+1)
#使用pandas的cut功能分箱
colBin = pd.cut(col,bins=break_points,labels=labels,include_lowest=True)
return colBin
#為年齡分箱:
cut_points = [90,140,190]
labels = ["low","medium","high","very high"]
data["LoanAmount_Bin"] = binning(data["LoanAmount"], cut_points, labels)
print(pd.value_counts(data["LoanAmount_Bin"], sort=False))
low 104
medium 273
high 146
very high 91
dtype: int64
解析以下這段代碼季春,首先定義了一個cut_points列表,里面的三個點(diǎn)用于把LoanAmount
字段分割成四個段消返,對應(yīng)地载弄,定義了labels列表,四個箱子(段)按貸款金額分別為低撵颊、中宇攻、高、非常高倡勇。然后把用于分箱的LoanAmount
字段逞刷,cut_points,labels傳入定義好的binning函數(shù)译隘。
binning函數(shù)中亲桥,首先拿到分箱字段的最小值和最大值,把這兩個點(diǎn)加入到break_points列表的一頭一尾固耘,這樣用于切割的所有端點(diǎn)就準(zhǔn)備好了题篷。如果labels沒有定義就默認(rèn)按0~段數(shù)-1命名箱子。 最后借助pandas提供的cut函數(shù)進(jìn)行分箱厅目。
cut函數(shù)原型是cut(x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False)
番枚,x是分箱字段,bins是區(qū)間端點(diǎn)损敷,right指示區(qū)間右端是否閉合葫笼,labels不解釋..retbins表示是否需要返回bins,precision是label存儲和顯示的精度拗馒,include_lowest指示第一個區(qū)間的左端是否閉合路星。
在把返回的列加入到data后,使用value_counts函數(shù)诱桂,統(tǒng)計了該列的各個離散值出現(xiàn)的次數(shù)洋丐,并且指定不需要對結(jié)果進(jìn)行排序。
想了解更多請閱讀 Pandas Reference (cut)
11.為分類變量編碼
有時挥等,我們會面對要改動分類變量的情況友绝。原因可能是:
有些算法(如logistic回歸)要求所有輸入項目是數(shù)字形式。所以分類變量常被編碼為0, 1….(n-1)
有時同一個分類變量可能會有兩種表現(xiàn)方式肝劲。如迁客,溫度可能被標(biāo)記為“High”郭宝, “Medium”, “Low”掷漱,“H”粘室, “l(fā)ow”。這里 “High” 和 “H”都代表同一類別卜范。同理育特, “Low” 和“l(fā)ow”也是同一類別。但Python會把它們當(dāng)作不同的類別先朦。
一些類別的頻數(shù)非常低,把它們歸為一類是個好主意犬缨。
這里我們定義了一個函數(shù)喳魏,以字典的方式輸入數(shù)值,用‘replace’函數(shù)進(jìn)行編碼
#使用Pandas replace函數(shù)定義新函數(shù):
def coding(col, codeDict):
colCoded = pd.Series(col, copy=True)
for key, value in codeDict.items():
colCoded.replace(key, value, inplace=True)
return colCoded
#把貸款狀態(tài)LoanStatus編碼為Y=1, N=0:
print('Before Coding:')
print(pd.value_counts(data["Loan_Status"]))
data["Loan_Status_Coded"] = coding(data["Loan_Status"], {'N':0,'Y':1})
print('\nAfter Coding:')
print(pd.value_counts(data["Loan_Status_Coded"]))
Before Coding:
Y 422
N 192
Name: Loan_Status, dtype: int64
After Coding:
1 422
0 192
Name: Loan_Status_Coded, dtype: int64
在coding函數(shù)中第二個參數(shù)是一個dict怀薛,定義了需要編碼的字段中每個值對應(yīng)的編碼刺彩。
實(shí)現(xiàn)上首先把傳入的列轉(zhuǎn)換為Series對象,copy參數(shù)表示是否要進(jìn)行復(fù)制枝恋,關(guān)于copy可以看我另一篇筆記:深拷貝與淺拷貝的區(qū)別创倔。
copy得到的Series對象不會影響到原本DataFrame傳入的列,所以可以放心修改焚碌,這里replace函數(shù)中的inplace參數(shù)表示是否直接在調(diào)用對象上作出更改畦攘,我們選擇是,那么colCoded對像在調(diào)用replace后就會被修改為編碼形式了十电。最后知押,把編碼后的列加入到data中,比較編碼前后的效果鹃骂。
想了解更多請閱讀 Pandas Reference (replace)
12. 在一個數(shù)據(jù)框的各行循環(huán)迭代
有時我們會需要用一個for循環(huán)來處理每行台盯。比方說下面兩種情況:
- 帶數(shù)字的分類變量被當(dāng)做數(shù)值。
- 帶文字的數(shù)值變量被當(dāng)做分類變量畏线。
先看看data表格的數(shù)據(jù)類型:
#檢查當(dāng)前數(shù)據(jù)類型:
data.dtypes
結(jié)果:
Gender object
Married object
Dependents object
Education object
Self_Employed object
ApplicantIncome int64
CoapplicantIncome float64
LoanAmount float64
Loan_Amount_Term float64
Credit_History float64
Property_Area object
Loan_Status object
LoanAmount_Bin category
Loan_Status_Coded int64
dtype: object
可以看到Credit_History這一列被當(dāng)作浮點(diǎn)數(shù)静盅,而實(shí)際上我們原意是分類變量。所以通常來說手動定義變量類型是個好主意寝殴。那這種情況下該怎么辦呢蒿叠?這時我們需要逐行迭代了。
首先創(chuàng)建一個包含變量名和類型的csv文件杯矩,讀取該文件:
#載入文件:
colTypes = pd.read_csv(r'F:\Datahack_Loan_Prediction\datatypes.csv')
print(colTypes)
feature type
0 Loan_ID categorical
1 Gender categorical
2 Married categorical
3 Dependents categorical
4 Education categorical
5 Self_Employed categorical
6 ApplicantIncome continuous
7 CoapplicantIncome continuous
8 LoanAmount continuous
9 Loan_Amount_Term continuous
10 Credit_History categorical
11 Property_Area categorical
12 Loan_Status categorical
載入這個文件之后栈虚,我們能對它的逐行迭代,然后使用astype函數(shù)來設(shè)置表格的類型史隆。這里把離散值字段都設(shè)置為categorical類型(對應(yīng)np.object)魂务,連續(xù)值字段都設(shè)置為continuous類型(對應(yīng)np.float)。
#迭代每行,指派變量類型粘姜。
#注鬓照,astype函數(shù)用于指定變量類型。
for i, row in colTypes.iterrows(): #i: dataframe索引; row: 連續(xù)的每行
if row['type']=="categorical" and row['feature']!="Loan_ID":
data[row['feature']]=data[row['feature']].astype(np.object)
elif row['type']=="continuous":
data[row['feature']]=data[row['feature']].astype(np.float)
print(data.dtypes)
Gender object
Married object
Dependents object
Education object
Self_Employed object
ApplicantIncome float64
CoapplicantIncome float64
LoanAmount float64
Loan_Amount_Term float64
Credit_History object
Property_Area object
Loan_Status object
dtype: object
可以看到現(xiàn)在信用記錄(Credit_History
)這一列的類型已經(jīng)變成了‘object’ 孤紧,這在Pandas中代表分類變量豺裆。
特別地,無論是中文教程還是原版英文教程這個地方都出錯了.. 中文教材代碼中判斷條件是錯的号显,英文教程中沒有考慮到Loan_ID
這個字段臭猜,由于它被設(shè)定為表格的索引,所以它的類型是不被考慮的押蚤。
想了解更多請閱讀 Pandas Reference (iterrows)