溫故
在基本數(shù)據(jù)管理部分率挣,主要是涉及到如何新建數(shù)據(jù)集清寇,并且對(duì)數(shù)據(jù)集中的變量和觀測(cè)值進(jìn)行提取和操作∽隹基本上用到了如下函數(shù)占哟,可以根據(jù)函數(shù)名回憶一下用法:
import pandas as pd
import numpy as np
from pandas import Series, DataFrame
pd.read_table()
# 假設(shè)新建一個(gè)數(shù)據(jù)框?yàn)閐f
df.head(), df.tail(), df.shape(), df.dtypes()
df['var'] = values # 新建變量
np.where(), np.logical_and, np.less, np.greater # 變量重編碼
df.index, df.columns, df.index.map, df.columns.map, df.index.rename, df.index.reanme # 變量重命名
df.isnull, df.notnull, df.dropna, df,fillna # 缺失值處理
pd.to_datetime# 日期值
df.astype # 數(shù)據(jù)類型轉(zhuǎn)換
df.sorte_index df.sort.values # 排序
pd.merge, pd.concat, pd.appedn # 合并數(shù)據(jù)集
df.ix[], df[], df.loc[] # 數(shù)據(jù)取子集
df.sample # 抽樣
知新,一個(gè)實(shí)際案例
這一次我們使用R語言實(shí)戰(zhàn)高級(jí)數(shù)據(jù)管理的案例:
要討論數(shù)值和字符處理函數(shù)酿矢,讓我們首先考慮一個(gè)數(shù)據(jù)處理問題榨乎。一組學(xué)生參加了數(shù)學(xué)、科學(xué)和英語考試瘫筐。為了給所有學(xué)生確定一個(gè)單一的成績(jī)衡量指標(biāo)蜜暑,需要將這些科目的成績(jī)組合起來。另外策肝,你還想將前20%的學(xué)生評(píng)定為A肛捍,接下來20%的學(xué)生評(píng)定為B,依次類推之众。最后拙毫,你希望按字母順序?qū)W(xué)生排序。數(shù)據(jù)如表5-1所示棺禾。
觀察此數(shù)據(jù)集缀蹄,馬上可以發(fā)現(xiàn)一些明顯的障礙。首先,三科考試的成績(jī)是無法比較的袍患。由于它們的均值和標(biāo)準(zhǔn)差相去甚遠(yuǎn)坦康,所以對(duì)它們求平均值是沒有意義的。你在組合這些考試成績(jī)之前诡延,必須將其變換為可比較的單元滞欠。其次,為了評(píng)定等級(jí)肆良,你需要一種方法來確定某個(gè)學(xué)生在前述得分上百分比排名筛璧。再次,表示姓名的字段只有一個(gè)惹恃,這讓排序任務(wù)復(fù)雜化了夭谤。為了正確地將其排序,需要將姓和名拆開巫糙。
如下介紹的函數(shù)大部分在Python自帶庫如math朗儒,內(nèi)置函數(shù)都有,但是都不是元素級(jí)別的参淹,也就是必須要寫一個(gè)顯性的循環(huán)函數(shù)醉锄,和numpy,pandas提供的相比效率相差1000倍以上浙值。
數(shù)學(xué)函數(shù)
數(shù)學(xué)函數(shù)主要由numpy提供恳不,避免用到Python的低效的內(nèi)置循環(huán),轉(zhuǎn)而使用C封裝高效的矢量化運(yùn)算开呐。用法都是np.func()
烟勋。
函數(shù) | 描述 |
---|---|
abs, fabs | 絕對(duì)值。非復(fù)數(shù)值筐付,用fabs |
sqrt | 各元素的平方根 |
square | 計(jì)算各元素的平方 |
exp | 計(jì)算個(gè)元素的指數(shù) |
log, log10, log2, log1p | 對(duì)數(shù)運(yùn)算 |
sign | 計(jì)算各元素的正負(fù)號(hào) |
ceil(x) | 不小于x的最小整數(shù) |
floor(x) | 不大于x的最小整數(shù) |
rint(x) | 將x四舍五入到最接近的整數(shù)卵惦,保留dtype |
cos, sin, tan | 余弦,正弦和正切 |
cosh, sinh, tanh | 雙曲余弦家妆,雙曲正弦和雙曲正切 |
arccos, arcsin, arctan | 反余弦鸵荠,反正弦和反正切 |
arccosh, arcsinh, arctanh | 反雙曲余弦,反雙曲正弦和反雙曲正切 |
我們經(jīng)常會(huì)用到這些函數(shù)對(duì)數(shù)據(jù)進(jìn)行變換伤极。當(dāng)然蛹找,你可能已經(jīng)忘記了三角函數(shù)對(duì)應(yīng)是什么圖形了,這個(gè)時(shí)候就可以嘗試一下自己作圖了哨坪。
%matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
x = np.arange(-2*np.pi, 2*np.pi, 0.01*np.pi)
y = np.cos(x)
plt.plot(x,y)
更多有關(guān)的numpy的數(shù)學(xué)函數(shù)可以看官方文檔的2.8.1節(jié)庸疾。
統(tǒng)計(jì)函數(shù)
pandas對(duì)象擁有一組常用的數(shù)學(xué)和統(tǒng)計(jì)方法,比如說最大值当编,最小值届慈,均值,中位數(shù),四分位數(shù)等金顿。用法是df.func()
臊泌。部分函數(shù)在numpy也有,因此是np.func
揍拆。
方法 | 說明 |
---|---|
desribe | 列計(jì)算匯總渠概,列出四分位數(shù)等信息 |
max,min | 最大值和最小值 |
idxmin, idxmax | 最大值和最小值的索引位置 |
quantile | 分位數(shù) |
sum | 求和 |
mean | 平均數(shù) |
median | 中位數(shù) |
mad | 根據(jù)平均值計(jì)算平均離差 |
var | 方差 |
std | 標(biāo)準(zhǔn)差 |
skew | 樣本值的偏度(三階矩) |
kurt | 樣本值的豐度(四階矩) |
cumsum | 樣本的累積和 |
cummin,cummax | 累計(jì)最大值和最小值 |
cumprod | 累積積 |
diff | 計(jì)算一階差分 |
pct_change | 計(jì)算百分比變化 |
簡(jiǎn)單案例:均值和標(biāo)準(zhǔn)差的計(jì)算
x = np.arange(1,10,1)
x.mean() # np.mean(x)
x.std() # np.std(x)
概率函數(shù)
根據(jù)定義嫂拴,概率函數(shù)也屬于統(tǒng)計(jì)類播揪,但是pandas沒有提供這類函數(shù)。這類函數(shù)用到一個(gè)專門的科學(xué)計(jì)算庫 scipy筒狠, 是一組專門解決科學(xué)計(jì)算中各種標(biāo)準(zhǔn)問題域的包的集合猪狈。
為了能更好的應(yīng)用,可能需要花一點(diǎn)功夫稍微講解一下 scipy 的 概率函數(shù)(stats)模塊辩恼。該模塊提供了常用的概率分布雇庙,大致分為兩類,連續(xù)型分布和離散型分布灶伊,這兩類分布都是 rv__continuous 和 rv_discrete 的子類状共。
比如說標(biāo)準(zhǔn)的正態(tài)函數(shù)分布失尖,norm, 作為 rv_continuous的子類耸峭,它覆寫了父類的pdf方法
norm.pdf(x) = exp(-x**2/2)/sqrt(2*pi)
用人類更加可讀的形式寫出來就是 $f(x) = \frac{1}{\sqrt(2\pi)} e{-\frac{x2}{2}}$ 也就是教科書上的定義方式笋额。如果查看他的均值和方差,也是熟悉的0和1. 也就是說通過rv__continuous 和 rv_discrete 這兩個(gè)父類還能夠構(gòu)造出 stats 模塊沒有提供分布函數(shù)匈挖。
子類繼承父類的方法,如 var, std, mean, median 提供分布的各統(tǒng)計(jì)量, expect 計(jì)算期望值康愤, stats 函數(shù)提供了moments參數(shù)用于指定需要計(jì)算的統(tǒng)計(jì)量儡循,m=mean, v=variance, s=Fisher's skew, k=Fisher's kurtosis.
R中的概率函數(shù)形如[dpqr] 分布函數(shù)英文縮寫,scipy.stats則是分布函數(shù)英文縮寫.方法
- rvs(): 根據(jù)概率分布征冷,返回隨機(jī)數(shù)
- pdf(x): 密度函數(shù)择膝, 對(duì)應(yīng)R的d
- cdf(x): 給定隨機(jī)變量(RV)的累積分布函數(shù)(),對(duì)應(yīng)R的p
- ppf(x): 分位數(shù)函數(shù)(quantile function) 對(duì)應(yīng)R的q
- sf(x): 生存函數(shù)(1-cdf)
常用的概率函數(shù)如下表
連續(xù)型分布 | 縮寫 | 離散型分布 | 縮寫 |
---|---|---|---|
Beta分布 | beta | 二項(xiàng)分布 | binorm |
柯西分布 | cauchy | 幾何分布 | geom |
卡方分布 | chi2 | 超幾何分布 | hypergeom |
指數(shù)分布 | expon | 負(fù)二項(xiàng)分布 | nbinom |
F分布 | f | 泊松分布 | poisson |
(通用) Gmamma分布 | (gen)gamma | - | - |
對(duì)數(shù)正態(tài)分布 | exponnorm | - | - |
(通用)正態(tài)分布 | (gen)nrom) | - | - |
其他相關(guān)的分布用到的時(shí)候查官方文檔就行了。
以熟悉的正態(tài)分布的有關(guān)函數(shù)和方法為例检激,了解這些函數(shù)的使用方法肴捉。如標(biāo)準(zhǔn)正態(tài)函數(shù)的密度函數(shù)(norm.pdf),分布函數(shù)(norm.ppf)叔收,隨機(jī)數(shù)生成函數(shù)
繪制標(biāo)準(zhǔn)正態(tài)曲線
from scipy.stats import norm
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
x = np.linspace(-3,3,30) # 非常熟悉的MATLAB齿穗,對(duì)應(yīng)R的pretty
y = norm.pdf(x) # 對(duì)應(yīng)R的dnrom
ax.plot(x,y,'r-', lw=5, alpha=0.6, label='norm pdf')
)
norm.cdf(1.96)
約等于0.975, norm.ppf(0.975)
與等于1.96饺律。
norm.rvs(siez=(4,4))
生成一個(gè)4X4的隨機(jī)數(shù)窃页,等同于np.random.normal(size=(4,4))
.
字符處理函數(shù)
Python本身的字符處理模塊是非常多的,比如說re用于正則,string是常用字符操作模塊脖卖, 字符串?dāng)?shù)據(jù)類型本身還有許多方法乒省。 我在簡(jiǎn)書上的一篇文章Python與R的異同(二):字符串操作 就介紹了兩則的異同。
這些函數(shù)無法直接套用到ndarry畦木, Series, DataFrame數(shù)據(jù)結(jié)構(gòu)中袖扛,需要用到專門的方法。不過馋劈,pandas提供了矢量化的字符串方法攻锰,更加高效。
方法 | 說明 |
---|---|
len | 計(jì)算字符數(shù)量 |
contains | 是否符合含指定模式的布爾型數(shù)組 |
count | 最大值和最小值的索引位置 |
findall | 計(jì)算各字符串的模式列表 |
cat | 實(shí)現(xiàn)元素級(jí)的字符串連接操作 |
get | 獲取第i個(gè)元素 |
lower, upper | 大小寫轉(zhuǎn)換 |
match | 元素級(jí)別的re.match |
split | 根據(jù)正則或指定分隔符進(jìn)行分割 |
strip, rstrip, lstrip | 去除空白符妓雾,包括換行符 |
join | 根據(jù)指定的分隔符進(jìn)行連接 |
repeat | 對(duì)字符進(jìn)行重復(fù) |
replace | 用指定的字符串替換找到的模式 |
以《利用Python做數(shù)據(jù)分析》的數(shù)據(jù)為例娶吞,介紹如何匹配字符,提取指定內(nèi)容械姻。
data = {'Dava':'dave@google.com','Steve':'steve@gmail.com','Rob':'rob@gmail.com','Wes':np.nan}
data = Series(data)
data.str.contains('gmail')
import re
# 提取姓名等信息
pattern = re.compile(r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.[A-Z]{2,4}', flags=re.IGNORECASE)
data.str.findall(pattern)
將函數(shù)應(yīng)用到Series和DataFrame中
我們介紹的數(shù)學(xué)函數(shù)和統(tǒng)計(jì)函數(shù)妒蛇,大部分都是元素級(jí)別的操作,也就是能夠直接應(yīng)用到一系列數(shù)據(jù)結(jié)構(gòu)上楷拳,包括Python自帶的序列數(shù)據(jù)結(jié)構(gòu)绣夺,list, tuple, numpy的ndarray,pandas提供的Series和DataFrame.
a = 5
np.sqrt(a) # 如果用math.sqrt(5)欢揖,效率相差100倍
b = np.array([1.243,5.654,2.99])
np.round(b) # 比[round(x) for x in b] 快5倍
c = np.random.uniform(size=(3,4))
np.log(c)
np.mean(c)
默認(rèn)情況下陶耍,np.mean
計(jì)算的是總體均值,如果你希望按行按列計(jì)算均值的話她混,可以簡(jiǎn)單的使用np.meanc(c, axis=1)
烈钞,axis的0表示按列,1表示按行坤按。還有一個(gè)比較通用的方法類似于R的apply函數(shù)毯欣。
numpy使用的apply_along_axis
,apply_over_axes
;pandas則是為數(shù)據(jù)框提供了apply
和applymap
方法. 這里演示pandas的apply
方法, 其他查看幫助文檔就行臭脓。
# df.apply(func, axis=0), func可以是自定義函數(shù)
data = DataFrame(c)
data.apply(np.mean, 1)
除了apply
外酗钞,pandas還允許用applymap
映射原來的Python的自帶函數(shù)成為元素級(jí)函數(shù). 比如說直接用math.ceil(data)
是不行的,需要用applymap
進(jìn)行映射来累。
data.applymap(lambda x : math.ceil(x)) #相比較np.ceil 效率相差80倍
解決問題
還記得之前提出的問題嗎, 先回顧一下砚作,然后用剛才學(xué)到的知識(shí)進(jìn)行解決。
將學(xué)生的各科考試成績(jī)組合為單一的成績(jī)衡量指標(biāo)嘹锁、基于相對(duì)名次(前20%偎巢,下20%,等等)給出從A到F的評(píng)分兼耀、根據(jù)學(xué)生姓氏和名字的首字母對(duì)花名冊(cè)進(jìn)行排序
第一步: 數(shù)據(jù)輸入
# 導(dǎo)入庫
import numpy as np
from pandas import Series, Dataframe
# 數(shù)據(jù)
roster = pd.read_csv("student_grade.txt", header=None,names=["Student","Math","Science", "English"])
第二步: 計(jì)算綜合評(píng)分压昼。計(jì)算綜合得分就是先對(duì)每一門學(xué)科的成績(jī)進(jìn)行標(biāo)準(zhǔn)化求冷,然后進(jìn)行相加。標(biāo)準(zhǔn)化的一種方法是歸一化窍霞,即將一組數(shù)據(jù)進(jìn)行均值為0匠题,標(biāo)準(zhǔn)差為1的標(biāo)準(zhǔn)化。
score = np.mean(roster.ix[:,1:].apply(lambda x : (x-np.mean(x))/np.std(x)), axis=1)
roster['score'] = score
這一步比較復(fù)雜的就是我用了匿名函數(shù)進(jìn)行標(biāo)準(zhǔn)化但金,然后用np.mean進(jìn)行計(jì)算均值韭山。
第三步: 計(jì)算四分位數(shù), 并且學(xué)生進(jìn)行評(píng)分
y = roster.score.quantile(q=[0.8,0.6,0.4,0.2])
grade = np.where(score > y.ix[0.8], 'A', np.where(score > y.ix[0.6], 'B', np.where(score > y.ix[0.4], 'D','F')))
roster['grade'] = grade
這一步用到了np.where進(jìn)行元素級(jí)別的判斷。冷溃、復(fù)制給grade列钱磅。
第四步: 根據(jù)學(xué)生姓氏和名字的首字母對(duì)花名冊(cè)進(jìn)行排序
first_name = roster.Student.apply(lambda x: x.split(sep=' ')[0])
last_name = roster.Student.apply(lambda x: x.split(sep=' ')[1])
roster['first_name'] = firt_name
roster['last_name'] = last_name
roster.drop('Student', axis=1, inplace=True)
同樣用到了匿名函數(shù), 對(duì)所有元素應(yīng)用字符處理函數(shù)似枕。
第五步: 排序
roster.sort_values(by=['first_name','last_name'], inplace=True)
最后結(jié)果如下: