本文章旨在更客观地看待这两门语言。我们会平行使用Python和R分析一个数据集,展示两种语言在实现相同结果时需要使用什么样的代码。这让我们了解每种语言的优缺点,而不是猜想。
我们将会分析一个NBA数据集,包含运动员和他们在2013-2014赛季的表现,可以在这里下载这个数据集。我们展示Python和R的代码,同时做出一些解释和讨论。
读取CSV文件
R
nba ->'nba_2013.csv')
Python
import pandasnba = pandas.read_csv('nba_2013.csv')
上面的代码分别在两种语言中将包含2013-2014赛季NBA球员的数据的nba_2013.csv文件加载为变量nba。Python中实际的唯一不同是需要加载pandas库以使用Dataframe。Dataframe在R和Python中都可用,它是一个二维数组(矩阵),其中每列都可以是不同的数据类型。在完成这一步后,csv文件在两种语言中都加载为dataframe。
统计球员数量
R
print(dim(nba))
[1] 481 31
Python
print(nba.shape)
(481, 31)
两者分别输出球员数量和数据列数量。我们有481行,或者说球员,和31列关于球员的数据。
查看数据的第一行
R
print(head(nba, 1))
player pos age bref_team_id1 Quincy Acy SF 23 TOT[output truncated]
Python
print(nba.head(1))
player pos age bref_team_id0 Quincy Acy SF 23 TOT[output truncated]
它们几乎完全相同。两种语言都打印出数据的第一行,语法也非常类似。Python在这里更面向对象一些,head是dataframe对象的一个方法,而R具有一个单独的head函数。当开始使用这些语言做分析时,这是一个共同的主题,可以看到Python更加面向对象而R更函数化。
计算每个指标的均值
让我们为每个指标计算均值。如你所见,数据列以类似fg(field goals made)和ast(assists)的名称命名。它们都是球员的赛季统计指标。如果想得到指标的完整说明,参阅这里。
R
meanNoNA ->
player NApos NAage 26.5093555093555bref_team_id NA[output truncated]
Python
import numpynba_numeric = nba._get_numeric_data()nba_numeric.apply(numpy,.mean, axis=0)
age 26.509356g 53.253638gs 25.571726[output truncated]
这里有一些明显的分歧。在两种方法中,我们均在dataframe的列上应用了一个函数。在python中,如果我们在非数值列(例如球员姓名)上应用函数,会返回一个错误。要避免这种情况,我们只有在取平均值之前选择数值列。
在R中,对字符串列求均值会得到NA——not available(不可用)。然而,我们在取均值时需要确实忽略NA(因此需要构建我们自己的函数)。否则类似x3p.这样的一些列的均值将会为NA,这一列代表三分球的比例。有些球员没有投出三分球,他们的百分比就是缺失的。如果我们直接使用R中的mean函数,就会得到NA,除非我们指定na.rm=TRUE,在计算均值时忽略缺失值。
绘制成对散点图
一个探索数据的常用方法是查看列与列之间有多相关。我们将会比较ast,fg和trb。
R
library(GGally)ggpairs(nba[, c('ast', 'fg', 'trb')])
import seaborn as snsimport matplotlib.pyplot as pltsns.pairplot(nba[['ast', 'fg', 'trb']])plt.show()
我们会得到非常相似的两张图,但是可以看到R的数据科学生态中有许多较小的软件包(GGally是最常用的R绘图包ggplot2的辅助包)和更多的通用可视化软件包。在Python中,matplotlib是主要的绘图包,seaborn是一个广泛用于matplotlib上的图层。Python中的可视化通常只有一种蛀牙哦的方法完成某件事,而R中可能有许多包支持不同的方法(例如,至少有半打绘制成对散点图的包)。
对球员聚类
另一个很好探索数据的方式是生成类别图。这将会显示哪些球员更相似。
R
library(cluster)set.seed(1)isGoodCol ->0 && is.numeric(col)}goodCols ->->5)labels ->
Python
from sklearn.cluster import KMeanskmeans_model = KMeans(n_clusters=5, random_state=1)good_columns = nba._get_numeric_data().dropna(axis=1)kmeans_model.fit(good_columns)labels = kmeans_model.labels_
为了正确的聚类,我们移除了所有非数值列,以及包含缺失值的列。在R中,我们在每一列上应用一个函数,如果该列包含任何缺失值或不是数值,则删除它。接下来我们使用cluster包实施k-means聚类,在数据中发现5个簇。通过set.seed设置随机种子以使结果可复现。
在Python中,我们使用了主要的Python机器学习包scikit-learn拟合k-means模型并得到类别标签。数据准备的过程和R非常类似,但是用到了get_numeric_data和dropna方法。
绘制类别图
我们现在可以按类别绘制球员分布图以发现模式。首先使用PCA将数据降至2维,然后画图,用不同标记或深浅的点标志类别。
nba2d ->->1:2]clusplot(twoColumns, labels)
Python
from sklearn.decomposition import PCApca_2 = PCA(2)plot_columns = pca_2.fit_transform(good_columns)plt.scatter(x=plot_columns[:,0], y=plot_columns[:,1], c=labels)plt.show()
在R中,我们通过聚类库中的函数clusplot函数绘图,使用内建函数pccomp实行PCA。
在Python中,我们使用scikit-learn库中的PCA类,使用matplotlib创建图形。
划分训练集和测试集
如果我们希望进行监督性机器学习,将数据划分为训练集和测试集是一个避免过拟合的好办法。
R
trainRowCount ->0.8 * nrow(nba))set.seed(1)trainIndex ->1:nrow(nba), trainRowCount)train ->->
Python
train = nba.sample(frac=0.8, random_state=1)test = nba.loc[~nba.index.isin(train.index)]
你能注意到R有更多的数据分析内建函数,例如floor,sample和set.seed,这些函数在Python中通过第三方库被调用(math.floor,random.sample,random.seed)。在Python中,最新版本的pandas包含一个sample方法,返回对原始dataframe确定比例的随机抽样,这使得代码更加简洁。在R中,有很多包可以使抽样更容易,但是没有一个比使用内置sample函数更简洁。在两个例子中,我们都设置了随机种子以保证结果的可重复性。
一元线性回归
假设我们希望通过球员的得分预测其助攻次数。
R
fit ->->
Python
from sklearn.linear_model import LinearRegressionlr = LinearRegression()lr.fit(train[['fg']], train['ast'])predictions = lr.predict(test[['fg']])
Scikit-learn包含一个线性回归模型,我们可以通过它拟合并生成预测。R依赖于内建函数lm和predict。predict根据传递给它拟合模型的不同会表现出不同的行为,它可以被用于各种各样的模型。
计算模型统计量
R
summary(fit)
Call:lm(formula = ast ~ fg, data = train)Residuals: Min 1Q Median 3Q Max -228.26 -35.38 -11.45 11.99 559.61[output truncated]
Python
import statsmodels.formula.api as smmodel = sm.ols(formula='ast ~ fga', data=train)fitted = model.fit()print(fitted.summary())
OLS Regression Results============================Dep. Variable: astR-squared: 0.568Model: OLSAdj. R-squared: 0.567[output truncated]
如果希望得到类似R平方值这样的模型统计量,在Python中需要比R多做一点。在R中,我们可以使用内建summary函数得到模型信息。在Python中,我们需要使用statsmodels包,这个包包含许多统计模型的Python实现。我们得到类似的结果,总体来说在Python中进行统计分析稍有点困难,一些R中存在的统计方法也没有存在于Python。
拟合一个随机森林模型
一元线性回归表现的不错,但是我们怀疑数据中可能存在非线性。因此,我们想要拟合一个随机森林模型。
R
library(randomForest)predictorColumns ->'age', 'mp', 'fg', 'trb', 'stl', 'blk')rf ->100)predictions ->
Python
from sklearn.ensemble import RandomForestRegressorpredictor_columns = ['age', 'mp', 'fg', 'trb', 'stl', 'blk']rf = RandomForestRegressor(n_estimators=100, min_samples_leaf=3)rf.fit(train[predictor_columns], train['ast'])predictions = rf.predict(test[predictor_columns])
这里主要的区别是R需要使用randomForest库实现算法,而Python中的scikit-learn直接内建其中。scikit-learn为许多不同的机器学习算法提供了统一的交互接口,在Python中每种算法通常只有一个主要的实现。而R中有许多包含单个算法较小的包,一般访问的方法并不一致。这导致算法更加的多样化(很多算法有多个实现,还有那些新问世的算法),但是只有一小部分是可用的。
计算误差
现在已经拟合了两个模型,下面让我们计算误差,使用MSE
R
mean((test['ast'] - predictions)^2)
4573.86778567462
Python
from sklearn.metrics import mean_squared_errormean_squared_error(test['ast'], predictions)
4166.9202475632374
Python中的scikit-learn库包含我们可以使用的各种误差量度。在R中,可能有一些小的第三方库计算MSE,但是两种语言中手动计算它都很容易。误差的细微差异几乎可以肯定是由于参数调整造成的,并没什么关系。
下载一个网页
现在已经有了2013-2014赛季的NBA球员数据,让我们抓取一些额外数据补充它。为了节省时间,在这里看一场NBA总决赛的比分。
R
library(RCurl)url ->'http://www.basketball-reference.com/boxscores/201506140GSW.html'page ->->->
Python
import requestsurl = 'http://www.basketball-reference.com/boxscores/201506140GSW.html'data = requests.get(url).content
Python中的requests包为所有的请求类型使用统一的API接口,下载网页非常容易。在R中,RCurl提供稍微复杂方法发起请求。两者都把网页下载为字符串类型的数据。注:这在R中的下一步并不是必须,只是为了比较的原因。
抽取球员比分
现在我们已经下载了网页,需要处理它以抽取球员比分。
R
library(rvest)page ->->'.stats_table')[3]rows ->'tr')cells ->'td a')teams ->->
if(i == 1){
return } row ->->'td' if(i == 2){ tag ->'th' } items ->->->'#',team,'_basic', sep='')) rows ->'tr') lapply(seq_along(rows), extractRow, rows=rows)}data ->
Python
from bs4 import BeautifulSoupimport resoup = BeautifulSoup(data, 'html.parser')box_scores = []for tag in soup.find_all(id=re.compile('[A-Z]{3,}_basic')): rows = [] for i, row in enumerate(tag.find_all('tr')):
if i == 0:
continue elif i == 1: tag = 'th' else: tag = 'td' row_data = [item.get_text() for item in row.find_all(tag)] rows.append(row_data) box_scores.append(rows)
这将创建一个包含两个列表的列表,第一个是CLE的比分,第二个是GSW的比分。两个都有标题,以及每个球员和他们的比赛统计。我们现在不会将其转换为更多的训练数据,但是如果需要把它们加入nbadataframe,转换可以很容易地完成。
R代码比Python更复杂,因为它没有一个方便的方式使用正则表达式选择内容,因此我们不得不做额外的处理以从HTML中得到队伍名称。R也不鼓励使用for循环,支持沿向量应用函数。我们使用lapply做到这一点,但由于需要处理的每一行都因是否是标题而异,需要传递保留项的索引和整个rows列表给函数。
我们使用rvest,一个广泛使用的新R网络抓取包实现抽取数据,注意这里可以直接传递url给rvest,因此上一步在R中并不是必须的。
在Python中,我们使用了BeautifulSoup,一个最常用的web抓取包。它让我们可以在标签间循环,并以一种直接的方式构建列表的列表。
结论
我们已经看到了如何使用R和Python分析一个数据集。还有很多任务没有深入,例如保存和分享分析结果,测试,确保生产就绪,以及构建更多的可视化。我们会在近期继续探讨这些,从而得到更明确的结论。现在,下面是一些能够得到的:
R更加函数化,Python更面向对象
就像我们在lm,predict和其他函数中看到的那样,R用函数完成大部分工作。对比Python中的`LinearRegression类,还有dataframe的sample方法。
R包含更多的数据分析内建功能,Python依赖于第三方软件包。
当我们查看汇总统计量时,在R中可以直接使用summary内建函数,但是Python中必须依靠statsmodels包。dataframe是R内置的结构,而在Python中由pandas包引入。
Python拥有“主要的”数据分析包,R拥有由较小的包组成的更大的生态系统
在Python中,我们可以使用scikit-learn完成线性回归,随机森林和许多其他任务。它提供了一致的API,并很好的维护。在R中,我们有多种多样的包,但是也更加碎片化和不一致(线性回归是内置的lm,randomForest是单独的包,等等)。
总体上R有更多的统计支持
R是作为统计语言被构建的,它也显示了这一点。Python中的statsmodels和其他软件包提供了统计方法的大部分实现,但是R的生态系统要大的多。
Python中完成非统计任务通常更加直接
有了类似BeautifulSoup和request这样良好维护的软件包,Python中的网页抓取远易于R。这种说法也适于我们还未关注的其他任务,例如保存数据库,部署web服务器或运行复杂的工作流。
数据分析工作流在两者之间有许多相似之处
R和Python之间有一些互相启发的地方(pandas的Dataframe受到R中dataframe的影响,rvest包来自BeautifulSoup的启发),两者的生态系统都在不断发展壮大,对两种语言中许多共同的任务来说,语法和实现都是非常相似的。
总结
在Dataquest,我们首先教授Python,但是最近也加入了R的课程。我们看到这两种语言是互补的,虽然Python在更多领域更强大,但R是一种高效的语言。它可以作为Python在数据探索和统计等领域的补充,或者你惟一的数据分析工具。正如本文中所显示的,两种语言有许多相似的语法和实现方法,你不能在一个或另一个,或者两者中出错。
原文:http://www.bigdata.ren/portal.php?mod=view&aid=823&page=4