数据科学家常犯的十大编程错误
2019年05月15日 由 sunlei 发表
901510
0
数据科学家是“比任何软件工程师都更擅长统计,比任何软件工程师都更擅长软件工程的的统计学家”。许多数据科学家都有统计学背景却缺乏在软件工程方面的经验。我是资深的数据科学家,在StackOverflow中python编码排名前1%。今天我们来聊聊我经常看到的很多(初级)数据科学家常犯的10个编程错误。
- 不要共享代码中引用的数据
数据科学需要代码和数据。因此,为了让其他人能够复制你的结果,他们需要访问数据,作为最最基本的这一点,但是很多人忘记与他们的代码共享数据。
import pandas as pd
df1 = pd.read_csv('file-i-dont-have.csv') # fails
do_stuff(df)
解决方案:使用d6tpipe与代码共享数据文件或上载到s3/web/google drive等或保存到数据库,以便收件人可以检索文件(但不要将其添加到Git中,请参见下文)。
- 硬编码的路径
与错误1类似,如果你的硬编码路径,其他人无法访问到,那么他们不能运行您的代码,必须在很多地方查找手动更改路径。
import pandas as pd
df = pd.read_csv('/path/i-dont/have/data.csv') # fails
do_stuff(df)
# or
impor os
os.chdir('c:\\Users\\yourname\\desktop\\python') #
fails
解决方案:使用相对路径、全局路径配置变量或d6tpipe使您的数据易于访问。
- 将数据与代码混合。
既然数据科学代码需要数据,为什么不把它放在相同的目录中呢?当你在那里的时候,保存的图像,报告和其他垃圾也在那里。哎呀,真是一团糟!
├── data.csv
├── ingest.py
├── other-data.csv
├── output.png
├── report.html
└── run.py
解决方案:将目录组织成不同类别,如数据、报告、代码等。参见Cookiecutter Data Science或d6tflow项目模板并使用#1中提到的工具来存储和共享数据。
- Git用源代码提交数据
大多数人现在控制他们的代码的版本(如果你不这样做的话就会犯另一个错误! !见git)。为了共享数据,可能很容易将数据文件添加到版本控制中。这对于很小的数来说是可以的,但是git没有针对数据进行优化,尤其是大型文件。
git add data.csv
解决方案:使用#1中提到的工具来存储和共享数据。如果你真正想要对数据进行版本控制,请参阅d6tpipe、dvc和Git大文件存储。
- 编写函数而不是DAGs
关于数据的讨论已经够多了,让我们来谈谈实际的代码吧!自从你学习编码时,首先要学习的是函数,数据科学代码主要由一系列运行的线性函数组成。这就导致了一些问题。机器学习代码可能不好。
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'])
解决方案:与其使用线性链接函数,不如将数据科学代码
编写为一组任务,并在这些任务之间建立依赖关系。使用d6tflow或airflow。
- 循环写入
就像函数一样,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]
# should be: scipy.stats.zscore(x)
# or
groupavg = []
for i in df['g'].unique():
dfg = df[df[g']==i]
groupavg.append(dfg['g'].mean())
# should be: df.groupby('g').mean()
解决方案:Numpy、scipy和panda为你认为可能需要循环的大部分内容提供向量化的函数。
- 不要编写单元测试
当数据、参数或用户输入发生变化时,您的代码可能会中断,有时你根本没有注意到,这可能会导致糟糕的产出。有人根据你的输出做出决定,坏数据会导致错误的决定!
解决方案:使用assert语句检查数据质量。panda有相等测试,d6tstack是否有数据摄取检查,d6tjoin检查数据连接。代码示例数据检查:
assert df['id'].unique().shape[0] == len(ids) # have
data for all ids?
assert df.isna().sum()<0.9 # catch missing values
assert df.groupby(['g','date']).size().max() ==1 # no
duplicate values/date?
assert d6tjoin.utils.PreJoin([df1,df2],
['id','date']).is_all_matched() # all ids matched?
- 不记录代码
我很明白你急着做分析,你急于一起把结果告诉你的客户或老板。一周后,他们
回来说“你能修改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
解决方案:即使是你交付了结果,也要多花点时间记录你所做的。你会感谢自己,其他人也会感谢你。这么做会让你更专业!
- 将数据保存为csv或pickle
备份数据,毕竟这是数据科学。就像函数和for循环、csv和pickle文件是常用的,但实际上它们也不是很好。CSV不包含模式,因此每个人都必须重新分析数字和日期。pickles解决了这个问题,但只在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或其他二进制数据格式。这在理想情况下是压缩数据的模式。d6tflowautomatically将任务的数据输出保存为parquet,所以你不需要处理它。
- 使用jupyter notebooks
让我们用一个有争议的结论来结束本文:jupyter notebooks作为CSV一样常见。很多人使用它们。但这不代表他们完美。jupyter notebooks助长了上面提到的很多不良的软件工程习惯,尤其是:
- 您试图将所有文件转储到一个目录中
- 你写的代码运行从上到下,而不是DAGs
- 您没有模块化您的代码
- 调试困难
- 代码和输出混合在一个文件中
- 他们不能很好地控制版本
开始做起来很容易,但是规模太小。
解决方案:使用pycharm或spyder。
好了,今天先聊到这里,以上就是我多年总结下来的常见的十个编程错误,希望对你们有帮助。