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