原文作者: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()
解决方案: Numpy、 scipy 、 pandas 提供了多种可以替代循环的向量化函数。
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 是上面提及的很多软件工程陋习的源泉,具体如下:
- 把所有文件都堆在一个文件夹里;
- 不用 DAG,而是编写由上向下的线性运行代码;
- 不利于模块化编程;
- 很难调试(debug)代码;
- 代码与输出结果都混在一个文件里;
- 版本控制不好。
总之,Jupyter Notebook 上手容易,深入难。
解决方案:使用 IDE,如,pycharm 、spyder、VSCode 。
文章到这里看似结束,但其实并没有结束,作者在原文里要表述的也不止这些,更多的还是想推广 Databolt 推出的 python 数据科学支持库, d6t-python。该支持库包括了几个组件,d6tflow 、d6tpipe 、d6tstack 、d6tjoin 。说明如下:
-
d6tflow,与 airflow 类似,用于控制数据分析工作的工作流,解决文中第 5、9 中的问题。
-
d6tpipe,创建公开或私有的远程文件存储,推送或取回数据文件,与他人共享数据文件,还可以管理多个项目的数据文件,解决文中第 1、2、4 中的问题。
-
d6tstack,用于提取数据,支持 xls、csv、txt 等源文件,可以输出为 csv、Parquet、SQL 与 pandas 格式的数据,提高了数据文件读取写入的性能,还可以检查与修复数据文件中的模式(Schema)问题。
-
d6tjoin,无需自己编写代码即可轻松 join 数据集,为字符串、日期与数字提供最佳匹配。比如,无需手动处理即可匹配相似但不相同的地址、姓名、日期。
呆鸟云:原文虽然有软广的嫌疑,但在很多方面也刺痛了呆鸟这颗数据分析老白的心,所以在正文里剔除了软广的内容,让大家可以专心阅读。
结尾加上了 d6t-Python 的介绍是因为虽然自己没用过,但这个支持库看起来也挺美,有兴趣的朋友可以亲手一试,也许会有惊喜哦。
网友评论