【呆鸟译Py】Stack Overflow 大咖告诉你数据分析师的10大编程陋习

0.978字数 1981阅读 93
Stack Overflow 专家告诉你数据分析师的10大编程陋习

原文作者:Norman Niemer
原文链接:Top 10 Coding Mistakes Made by Data Scientists

数据分析师是“比软件工程师更懂统计学,比统计学家更懂软件工程的人”。大部分数据分析师都有统计学背景,但软件工程的经验相对会比较少。我本人是一名高级数据科学家,曾与很多初级数据分析师共事过,同时,我也是 Stack Overflow 里 Python 版块的积极分子,活跃排名在前 1%,下面是我多年经验总结出来的,初级数据分析师最常见的 10 大陋习。

1. 不分享代码里引用的数据

数据分析需要编程与数据。对于想重现开发结果的人来说,他们需要访问数据文件。这说起来只是基础知识,但很多初级数据分析师都会忘记分享代码里引用的数据文件。

import pandas as pd
df1 = pd.read_csv('file-i-dont-have.csv') # 这里会出错
do_stuff(df)

解决方案:把数据文件上传到网盘,或者把数据保存至数据库,供读者提取,但不要把文件放到 git 上,原因见下文。

2. 直接写绝对路径,无法访问

与第 1 个错误类似,代码里的路径是绝对路径,无法访问。运行你的代码时,如果需要在很多地方修改路径,这就太麻烦了。

import pandas as pd
df = pd.read_csv('/path/i-dont/have/data.csv') # 这里会出错
do_stuff(df)

# 或
impor os
os.chdir('c:\\Users\\yourname\\desktop\\python') # 这里会出错

解决方案:使用相对路径,或用全局变量设置路径。

3. 把数据与代码放在一起

数据分析代码需要数据,凭什么不能把数据与代码放在同一个目录里?那是不是还可以把图片、报告或其它垃圾文件都放在一起,呕!真是一团糟~

├── data.csv
├── ingest.py
├── other-data.csv
├── output.png
├── report.html
└── run.py

解决方案:把文件分门别类放在不同目录里,比如,data、reports、code、images 等。

4. Git Commit 时用源代码提交数据

现在大家都习惯用版本控制工具提交代码,不过用版本控制工具分享数据就没那么么合适了。分享的数据文件比较小还好说,但 Git 并没有为数据,尤其是对大型数据文件做过优化。

git add data.csv

解决方案:把大型数据文件存在网盘里。

5. 不用DAG(有向无环图),用函数

数据问题先说到这里,接下来说下写代码问题!很多数据分析师都学过怎么写函数,数据分析代码大都是以线性方式运行的函数。这会引发一系列问题,详见《你的机器学习代码有问题的 4 大原因》

def process_data(data, parameter):
    data = do_stuff(data)
    data.to_pickle('data.pkl')

data = pd.read_csv('data.csv')
process_data(data)
df_train = pd.read_pickle(df_train)
model = sklearn.svm.SVC()
model.fit(df_train.iloc[:,:-1], df_train['y'])

解决方案:不要写成线性的链式函数,数据分析代码最好是完成各种任务的依赖项这种形式,建议了解下 airflow

6. 写循环

与函数一样,For 循环也是数据分析师的编程必修课,For 循环简单易懂,上手容易,但速度慢,写法也特别啰嗦,数据分析师写循环,只能说明他们不懂向量化编程。

x = range(10)
avg = sum(x)/len(x)
std = math.sqrt(sum((i-avg)**2 for i in x)/len(x))
zscore = [(i-avg)/std for x]
# 应该用: scipy.stats.zscore(x)

# 或

groupavg = []

for i in df['g'].unique():
    dfg = df[df['g'] == i]
    groupavg.append(dfg['g'].mean())
# 应该用: df.groupby('g').mean()

解决方案Numpyscipypandas 提供了多种可以替代循环的向量化函数。

7. 不写单元测试

数据、参数、用户输入的变化都会导致代码崩溃,很多时候,出了问题你都注意不到。输出结果有问题,以此做出的决策同样也会有问题,请记住,有问题的数据只会导致有问题的决策!

assert df['id'].unique().shape[0] == len(ids) # 所有数据是否都 id?
assert df.isna().sum()<0.9 # 捕获缺失值
assert df.groupby(['g','date']).size().max() ==1 # 是否有重复值或日期?
assert d6tjoin.utils.PreJoin([df1,df2],['id','date']).is_all_matched() # 所有 id 都匹配吗?

解决方案:使用 assert 语句检查数据质量。 pandas 有质量检测的功能,数据分析师可以尝试一下。

8. 不写文档说明

数据分析师干的都是急活儿,这个我懂。客户和领导都是急脾气,一有需求,你就得赶紧把问题搞定。但是过些日子,他们问“能不能调整下 XYZ”或让你“更新一下”的时候,你看着自己写的代码,都不记得自己为什么这么写了,这时就该傻眼了。

def some_complicated_function(data):
    data = data[data['column']!='wrong']
    data = data.groupby('date').apply(lambda x: complicated_stuff(x))
    data = data[data['value']<0.9]
    return data

解决方案:花些时间,哪怕是提交了分析以后,也要记录一下所做的工作。将来的你一定会感谢现在努力的自己!这样做,你的代码也会更专业!

9. 把文件保存为 CSV 或 Pickle

我们再把目光转回到数据,毕竟我要讲的是数据分析。与前面说过的循环与函数一样,CSV 与 Pickle 文件是数据分析师最喜欢的文件格式,但其实这两种格式都没那么好。CSV 不支持模式(Schema),导入数据后还要重新解析数字与日期。Pickle 文件解决了这个问题,但只能用于 Python,还不能压缩。这两种格式都不适合存储大型数据集。

def process_data(data, parameter):
    data = do_stuff(data)
    data.to_pickle('data.pkl')

data = pd.read_csv('data.csv')
process_data(data)
df_train = pd.read_pickle(df_train)

解决方案:用 parquet 或其它带数据模式(Schema)的二进制格式,最好是能压缩数据的格式。

10. 依赖 Jupyter Notebook

这一点争议很大:Jupyter Notebook 与 CSV 一样都是数据分析师的最爱,但这并不代表它们就非常好了。实际上,Jupyter Notebook 是上面提及的很多软件工程陋习的源泉,具体如下:

  1. 把所有文件都堆在一个文件夹里;
  2. 不用 DAG,而是编写由上向下的线性运行代码;
  3. 不利于模块化编程;
  4. 很难调试(debug)代码;
  5. 代码与输出结果都混在一个文件里;
  6. 版本控制不好。

总之,Jupyter Notebook 上手容易,深入难。

解决方案:使用 IDE,如,pycharmspyderVSCode


文章到这里看似结束,但其实并没有结束,作者在原文里要表述的也不止这些,更多的还是想推广 Databolt 推出的 python 数据科学支持库, d6t-python。该支持库包括了几个组件,d6tflowd6tpiped6tstackd6tjoin 。说明如下:

  • d6tflow,与 airflow 类似,用于控制数据分析工作的工作流,解决文中第 5、9 中的问题。

  • d6tpipe,创建公开或私有的远程文件存储,推送或取回数据文件,与他人共享数据文件,还可以管理多个项目的数据文件,解决文中第 1、2、4 中的问题。

  • d6tstack,用于提取数据,支持 xls、csv、txt 等源文件,可以输出为 csv、Parquet、SQL 与 pandas 格式的数据,提高了数据文件读取写入的性能,还可以检查与修复数据文件中的模式(Schema)问题。

  • d6tjoin,无需自己编写代码即可轻松 join 数据集,为字符串、日期与数字提供最佳匹配。比如,无需手动处理即可匹配相似但不相同的地址、姓名、日期。

d6t-python

呆鸟云:原文虽然有软广的嫌疑,但在很多方面也刺痛了呆鸟这颗数据分析老白的心,所以在正文里剔除了软广的内容,让大家可以专心阅读。

结尾加上了 d6t-Python 的介绍是因为虽然自己没用过,但这个支持库看起来也挺美,有兴趣的朋友可以亲手一试,也许会有惊喜哦。