我的第一份数据科学实习

Python019

我的第一份数据科学实习,第1张

我的第一份数据科学实习

在写本文时,这是我在Quantum Inventions公司实习的最后一天。当我坐在电脑屏幕前,反思过去几个月的学习历程,我感到非常的满足。

在实习即将结束时,萦绕在我脑中的问题是:我学到了些什么?这是我想要的吗?

作为一名物理学家,我习惯通过推理解答问题从而来寻求真相。事实上,对数据科学家来说,问合适的问题也是至关重要的。

本文分为三个部分:实习前,实习期间,实习后。当中记叙了在这段经历中我所得到的收获。也欢迎阅读我上一篇文章《我是如何从物理学转行到数据科学领域》。

实习前

我仍然清楚地记得,在2017年11月的期末时我开始阅读《统计学习导论:基于R应用》(An Introduction to Statistical Learning?—?with Applications in R)。这是第一次接触到基础的机器学习知识。

一旦掌握了这些概念之后,我就开始学习热门的课程Coursera上吴恩达的《机器学习》。虽然刚开始的时候并不那么轻松,但是吴恩达能够吸引人们的注意力,用简单的方式解读复杂的概念。这也许就是我喜欢上机器学习的原因之一。我也强烈建议你试试,机器学习并没有听上去那么高大上和复杂。

同时,我开始学习人工智能的另一个重要领域——深度学习。

在掌握了一定的基础知识之后,我在2017年12月开始了我的第一份数据科学实习。

实习期间

Quantum Inventions专注于向消费者、企业和政府智能交通服务。我是第一位加入研发和分析团队的数据科学实习生。

在刚开始的几天,我认识到了许多出色的同事,接触到许多行业术语以及正在进行的项目。在这次实习中,Quantum Inventions给了我足够的信任和自由,让我能够选择感兴趣的项目,并为其而努力。

令我吃惊的是,目前我着手的这个项目是之前没有人做过的。这时就需要大量的研究,存在许多不确定性和困难,但是我仍然乐在其中。为什么呢?很简单,因为我能够有机会从头开始体验整个数据科学工作流程。

下面我将列出我所经历的工作流程,在这一过程中为我进入数据科学领域打下了一定的基础。希望能够给你带来一些帮助。

1. 了解业务问题

我所选的项目是预测短期高速公路行程时间。正如我所说的,提出合适的问题对数据科学家来说非常重要。 在项目完成前,需要提出大量的问题,从而了解实际的业务问题:可用的数据源,项目的最终目标等等。我们的目标是更准确地预测新加坡高速公路行程时间。

2. 收集数据源

我对新项目感到十分兴奋,然后我开始从数据库等渠道收集数据源。收集正确的数据源类似在不同网站抓取数据,以便稍后进行数据预处理。这一过程非常重要,这可能会影响你在后期建立模型的准确性。

3. 数据预处理

真实世界的数据并不那么理想。我们不能期望会获得Kaggle比赛中那样格式良好且干净的数据。因此,数据预处理(也称为数据管理或数据清理)至关重要。该过程占到整个工作流程的40%-70%,对提供给模型的数据进行清理。

Garbage in, Garbage out。(无用输入,无用输出)

我喜欢数据科学的一点在于,你必须对自己诚实。如果你没有把握,认为预处理的数据已经足够干净,并可以提供给模型,那么将存在使用错误数据构建模型的风险。换句话说,你需要从专业的角度质疑自己,确认数据是否可以使用。严格用阈值检查数据,确认整个数据集中是否存在其他异常、缺失或不一致的数据。

我对这个过程格外谨慎,之前我仅仅因为预处理步骤中的小疏忽就给模型提供了错误的数据。

4. 建立模型

经过一番研究,对于项目我提出了以下四个模型:支持向量回归(SVR),多层感知器(MLP),长期短期记忆网咯(LSTM)和状态空间神经网络(SSNN)。

对于还在学习在线课程的我来说,从头开始构建不同的模型无疑是十分困难的。幸运的是,Scikit-learn and Keras拯救了我,因为它们很容易学习,能够快速构建原型模型,并用Python进行编写。此外,我还学习了如何使用多种技术优化模型,并调整每个模型的参数。

5. 模型评估

为了评估每个模型的性能,我主要使用了以下指标:

· 平均绝对误差(MAE)

· 均方误差(MSE)

· 测定系数(R2)

在这个阶段,重复步骤3-5(可互换),直到得出的最佳模型能够超越基线估计。

实习后

这次实习加深了我对数据科学领域的热爱,这段经历为我之后的工作也提供了一些动力。我也大大提高了自身的能力,比如与不同人群进行利益的沟通技巧,利用数据解决业务问题等等。

数据科学行业还是一个新兴领域,对于我们这些求职者来说,有时当中的一些工作描述可能有些模棱两可。因此在求职时,发现自己不具备岗位所需的全部技能是完全正常的,因为大多数工作描述都是按照理想的方式写的,从而符合雇主的期望。

当在学习或工作中遇到疑问时,你可以从在线课程、书籍和文章中进行学习,这也是我仍在做的,并通过个人项目或实习来应用所学到的知识。请耐心一点,学习的过程需要大量的精力和时间。

在最后,记得问自己:你学到了什么?这是你要的吗?

利用python进行线性回归

理解什么是线性回归

线性回归也被称为最小二乘法回归(Linear Regression, also called Ordinary Least-Squares (OLS) Regression)。它的数学模型是这样的:

y = a+ b* x+e

其中,a 被称为常数项或截距;b 被称为模型的回归系数或斜率;e 为误差项。a 和 b 是模型的参数。

当然,模型的参数只能从样本数据中估计出来:

y'= a' + b'* x

我们的目标是选择合适的参数,让这一线性模型最好地拟合观测值。拟合程度越高,模型越好。

那么,接下来的问题就是,我们如何判断拟合的质量呢?

这一线性模型可以用二维平面上的一条直线来表示,被称为回归线。

模型的拟合程度越高,也即意味着样本点围绕回归线越紧密。

如何计算样本点与回归线之间的紧密程度呢?

高斯和勒让德找到的方法是:被选择的参数,应该使算出来的回归线与观测值之差的平房和最小。用函数表示为:

这被称为最小二乘法。最小二乘法的原理是这样的:当预测值和实际值距离的平方和最小时,就选定模型中的两个参数(a 和 b)。这一模型并不一定反映解释变量和反应变量真实的关系。但它的计算成本低;相比复杂模型更容易解释。

模型估计出来后,我们要回答的问题是:

我们的模型拟合程度如何?或者说,这个模型对因变量的解释力如何?(R2)

整个模型是否能显著预测因变量的变化?(F 检验)

每个自变量是否能显著预测因变量的变化?(t 检验)

首先回答第一个问题。为了评估模型的拟合程度如何,我们必须有一个可以比较的基线模型。

如果让你预测一个人的体重是多少?在没有任何额外信息的情况下,你可能会用平均值来预测,尽管会存在一定误差,但总比瞎猜好。

现在,如果你知道他的身高信息,你的预测值肯定与平均值不一样。额外信息相比平均值更能准确地预测被预测的变量的能力,就代表模型的解释力大小。

上图中,SSA 代表由自变量 x 引起的 y 的离差平方和,即回归平方和,代表回归模型的解释力;SSE 代表由随机因素引起的 y 的离差平方和,即剩余平方和,代表回归模型未能解释的部分;SST 为总的离差平方和,即我们仅凭 y 的平均值去估计 y 时所产生的误差。

用模型能够解释的变异除以总的变异就是模型的拟合程度:

R2=SSA/SST=1-SSE

R2(R 的平方)也被称为决定系数或判定系数。

第二个问题,我们的模型是否显著预测了 y 的变化?

假设 y 与 x 的线性关系不明显,那么 SSA 相对 SSE 占有较大的比例的概率则越小。换句话说,在 y 与 x 无线性关系的前提下,SSA 相对 SSE 的占比越高的概率是越小的,这会呈现一定的概率分布。统计学家告诉我们它满足 F 分布,就像这样:

如果 SSA 相对 SSE 占比较大的情况出现了,比如根据 F 分布,这个值出现的概率小于 5%。那么,我们最好是拒绝 y 与 x 线性关系不显著的原始假设,认为二者存在显著的线性关系较为合适。

第三个问题,每个自变量是否能显著预测因变量的变化?换句话说,回归系数是否显著?

回归系数的显著性检验是围绕回归系数的抽样分布(t 分布)来进行的,推断过程类似于整个模型的检验过程,不赘言。

实际上,对于只有一个自变量的一元线性模型,模型的显著性检验和回归系数的检验是一致的,但对于多元线性模型来说,二者就不能等价了。

利用 statsmodels 进行最小二乘回归

#导入相应模块

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: import statsmodels.api as sm

#将数据导入 pandas 的 dataframe 对象,第一列(年份)作为行标签

In [4]: df=pd.read_csv('/Users/xiangzhendong/Downloads/vincentarelbundock-Rdatasets-1218370/csv/datasets/longley.csv', index_col=0)

#查看头部数据

In [5]: df.head()

Out[5]:

GNP.deflator      GNP  Unemployed  Armed.Forces  Population  Year  \

1947          83.0  234.289       235.6         159.0     107.608  1947

1948          88.5  259.426       232.5         145.6     108.632  1948

1949          88.2  258.054       368.2         161.6     109.773  1949

1950          89.5  284.599       335.1         165.0     110.929  1950

1951          96.2  328.975       209.9         309.9     112.075  1951

Employed

1947    60.323

1948    61.122

1949    60.171

1950    61.187

1951    63.221

#设置预测变量和结果变量,用 GNP 预测 Employed

In [6]: y=df.Employed #结果变量

In [7]: X=df.GNP #预测变量

#为模型增加常数项,即回归线在 y 轴上的截距

In [8]: X=sm.add_constant(X)

#执行最小二乘回归,X 可以是 numpy array 或 pandas dataframe(行数等于数据点个数,列数为预测变量个数),y 可以是一维数组(numpy array)或 pandas series

In [10]: est=sm.OLS(y,X)

使用 OLS 对象的 fit() 方法来进行模型拟合

In [11]: est=est.fit()

#查看模型拟合的结果

In [12]: est.summary()

Out[12]:

#查看最终模型的参数

In [13]: est.params

Out[13]:

const    51.843590

GNP       0.034752

dtype: float64

#选择 100 个从最小值到最大值平均分布(equally spaced)的数据点

In [14]: X_prime=np.linspace(X.GNP.min(), X.GNP.max(),100)[:,np.newaxis]

In [15]: X_prime=sm.add_constant(X_prime)

#计算预测值

In [16]: y_hat=est.predict(X_prime)

In [17]: plt.scatter(X.GNP, y, alpha=0.3) #画出原始数据

#分别给 x 轴和 y 轴命名

In [18]: plt.xlabel("Gross National Product")

In [19]: plt.ylabel("Total Employment")

In [20]: plt.plot(X_prime[:,1], y_hat, 'r', alpha=0.9) #添加回归线,红色

多元线性回归(预测变量不止一个)

我们用一条直线来描述一元线性模型中预测变量和结果变量的关系,而在多元回归中,我们将用一个多维(p)空间来拟合多个预测变量。下面表现了两个预测变量的三维图形:商品的销量以及在电视和广播两种不同媒介的广告预算。

数学模型是:

Sales = beta_0 + beta_1*TV + beta_2*Radio

图中,白色的数据点是平面上的点,黑色的数据点事平面下的点。平面的颜色是由对应的商品销量的高低决定的,高是红色,低是蓝色。

利用 statsmodels 进行多元线性回归

In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: import statsmodels.api as sm

In [4]: df_adv=pd.read_csv('g.csv',index_col=0)

In [6]: X=df_adv[['TV','Radio']]

In [7]: y=df_adv['Sales']

In [8]: df_adv.head()

Out[8]:

TV  Radio  Newspaper  Sales

1  230.1   37.8       69.2   22.1

2   44.5   39.3       45.1   10.4

3   17.2   45.9       69.3    9.3

4  151.5   41.3       58.5   18.5

5  180.8   10.8       58.4   12.9

In [9]: X=sm.add_constant(X)

In [10]: est=sm.OLS(y,X).fit()

In [11]: est.summary()

Out[11]:

你也可以使用 statsmodels 的 formula 模块来建立多元回归模型

In [12]: import statsmodels.formula.api as smf

In [13]: est=smf.ols(formula='Sales ~ TV + Radio',data=df_adv).fit()

处理分类变量

性别或地域都属于分类变量。

In [15]: df= pd.read_csv('httd.edu/~tibs/ElemStatLearn/datasets/SAheart.data', index_col=0)

In [16]: X=df.copy()

利用 dataframe 的 pop 方法将 chd 列单独提取出来

In [17]: y=X.pop('chd')

In [18]: df.head()

Out[18]:

sbp  tobacco   ldl  adiposity  famhist  typea  obesity  alcohol  \

row.names

1          160    12.00  5.73      23.11  Present     49    25.30    97.20

2          144     0.01  4.41      28.61   Absent     55    28.87     2.06

3          118     0.08  3.48      32.28  Present     52    29.14     3.81

4          170     7.50  6.41      38.03  Present     51    31.99    24.26

5          134    13.60  3.50      27.78  Present     60    25.99    57.34

age  chd

row.names

1           52    1

2           63    1

3           46    0

4           58    1

5           49    1

In [19]: y.groupby(X.famhist).mean()

Out[19]:

famhist

Absent     0.237037

Present    0.500000

Name: chd, dtype: float64

In [20]: import statsmodels.formula.api as smf

In [21]: df['famhist_ord']=pd.Categorical(df.famhist).labels

In [22]: est=smf.ols(formula="chd ~ famhist_ord", data=df).fit()

分类变量的编码方式有许多,其中一种编码方式是虚拟变量编码(dummy-encoding),就是把一个 k 个水平的分类变量编码成 k-1 个二分变量。在 statsmodels 中使用 C 函数实现。

In [24]: est=smf.ols(formula="chd ~ C(famhist)", data=df).fit()

In [26]: est.summary()

Out[26]:

处理交互作用

随着教育年限(education)的增长,薪酬 (wage) 会增加吗?这种影响对男性和女性而言是一样的吗?

这里的问题就涉及性别与教育年限的交互作用。

换言之,教育年限对薪酬的影响是男女有别的。

#导入相关模块

In [1]: import pandas as pd

In [2]: import numpy as np

In [4]: import statsmodels.api as sm

#导入数据,存入 dataframe 对象

In [5]: df=pd.read_csv('/Users/xiangzhendong/Downloads/pydatafromweb/wages.csv')

In [6]: df[['Wage','Education','Sex']].tail()

Out[6]:

Wage  Education  Sex

529  11.36         18    0

530   6.10         12    1

531  23.25         17    1

532  19.88         12    0

533  15.38         16    0

由于性别是一个二分变量,我们可以绘制两条回归线,一条是 sex=0(男性),一条是 sex=1(女性)

#绘制散点图

In [7]: plt.scatter(df.Education,df.Wage, alpha=0.3)

In [9]: plt.xlabel('education')

In [10]: plt.ylabel('wage')

#linspace 的作用是生成从最小到最大的均匀分布的 n 个数

In [17]: education_linspace=np.linspace(df.Education.min(), df.Education.max(),100)

In [12]: import statsmodels.formula.api as smf

In [13]: est=smf.ols(formula='Wage ~ Education + Sex', data=df).fit()

In [18]: plt.plot(education_linspace, est.params[0]+est.params[1]education_linspace+est.params[2]0, 'r')

In [19]: plt.plot(education_linspace, est.params[0]+est.params[1]education_linspace+est.params[2]1, 'g')

以上两条线是平行的。这是因为分类变量只影响回归线的截距,不影响斜率。

接下来我们可以为回归模型增加交互项来探索交互效应。也就是说,对于两个类别,回归线的斜率是不一样的。

In [32]: plt.scatter(df.Education,df.Wage, alpha=0.3)

In [33]: plt.xlabel('education')

In [34]: plt.ylabel('wage')

#使用*代表我们的回归模型中除了交互效应,也包括两个变量的主效应;如果只想看交互效应,可以用:代替,但通常不会只看交互效应

In [35]: est=smf.ols(formula='Wage ~ Sex*Education', data=df).fit()

In [36]: plt.plot(education_linspace, est.params[0]+est.params[1]0+est.params[2]education_linspace+est.params[3]0education_linspace, 'r')

In [37]: plt.plot(education_linspace, est.params[0]+est.params[1]1+est.params[2]education_linspace+est.params[3]1education_linspace, 'g')

参考资料:

DataRobot | Ordinary Least Squares in Python

DataRoboe | Multiple Regression using Statsmodels

AnalyticsVidhya | 7 Types of Regression Techniques you should know!

线上运行以及绘图效果,欢迎移步 和鲸社区

#terminal里

pip install plotly

http://www.plot.ly/python/getting-started

import plotly.offline as py

import plotly.graph_objs as go

trace = {'x':[1,2], 'y':[1,2]}

data = [trace]

layout = {}

fig = go.Figure(

    data = data, layout = layout)

py.offline.plot(fig)

trace1 = go.Scatter(

    x = [1,2], y = [1,2])

trace2 = go.Scatter(

    x = [1,2], y = [2,2])

py.iplot([trace1,trace2])

trace1 = go.Scatter(

    x = [1,2,3], y = [1,2,3],

    text = ['A','B','C'],

    textposition = 'top center',

    mode = 'markers+text')

data = [trace]

py.iplot(data)

trace = go.Bar(

    x = [1,2],

    y = [1,2],)

data = [trace]

py.iplot(data)

trace = go.Scatter(

    x = [1,2,3],

    y = [1,2,3],

    marker = dict(

        color = ['red','blue','green'],

        size = [30,80,200]),

    mode = 'markers'

)

py.iplot([trace])

trace = go.Heatmap(

    z = [[1,2,3,4],

    [5,6,7,8]])

data = [trace]

py.iplot(data)

trace = go.Scatter(

    x = [1,2,6],

    y = [1,2,0.5],

    fill = "tonexty")

data = [trace]

py.iplot(data)

trace1 = go.Scatter(

    name = "Calvin",

    x = [1,2],

    y = [2,1])

trace2 = go.Scatter(

    name = "Hobbes",

    x = [2,1],

    y = [2,1])

layout = go.Layout(

    showlegend = True,

    # 设置图例相对于左下角的位置

    legend = dict(

        x = 0.2,

        y = 0.5))

data = [trace1, trace2]

fig = go.Figure(data = data, layout = layout)

py.iplot(fig)

trace = go.Scatter(

    x = [-1,1,2,3,4],

    y = [-1,1,2,3,6])

axis_template = dict(

    showgrid = True,  #网格

    zeroline = True,  #是否显示基线,即沿着(0,0)画出x轴和y轴

    nticks = 20,

    showline = True,

    title = 'X axis',

    mirror = 'all')

layout = go.Layout(

    xaxis = axis_template,

    yaxis = axis_template)

data = [trace]

fig = go.Figure(

    data = data,

    layout = layout)

py.iplot(fig)

trace = go.Histogram(

    x = [1,2,3,3,3,4,5])

data = [trace]

py.iplot(data)

trace=go.Box(

    x=[1,2,3,3,3,4,5])

data=[trace]

py.iplot(data)

trace=go.Histogram2d(

    x=[1,2,3,3,3,4,5],

    y=[1,2,3,3,3,4,5])

data=[trace]

py.iplot(data)

trace = dict(

    type = 'scattergeo',

    lon = [100,400],lat = [0,0],

    marker = dict(

        color = ['red','blue'],

        size = [30,50]),

    mode = 'markers')

py.iplot([trace])

import plotly.colors

trace = dict(

    type = 'choropleth',

    locations = ['AZ','CA','VT'],

    locationmode = 'USA-states',

    colorscale = 'Viridis',

    z = [10,20,40])

layout = dict(geo = dict(scope = 'usa'))

map = go.Figure(data = [trace], layout = layout)

py.iplot(map)

trace=go.Surface(

    colorscale='Viridis',

    z=[[3,5,8,13],

    [21,13,8,5]])

data = [trace]

py.iplot(data)

trace = go.Scatter3d(

    x = [9,8,5,1],

    y = [1,2,4,8],

    z = [11,8,15,3],

    mode = 'lines')

data = [trace]

py.iplot(data)

trace = go.Scatter3d(

    x = [9,8,5,1],

    y = [1,2,4,8],

    z = [11,8,15,3],

    mode = 'markers')

data = [trace]

py.iplot(data)

Figure {}

data []

  trace {}

  x,y,z []

  color,text,size []

  colorscale ABC or []

  marker {}

    color ABC

    symbol ABC

    line {}

    color ABC

    width 123

layout {}

title ABC

xaxis,yaxis {}

scene {}

  xaxis,yaxis,zaxis {}

geo {}

legend {}

annotations {}

{} 字典

[] 列表

ABC 字符

123 数字

plotly使用指南

plot.ly