通过应用软件工程最佳实践,可以交付质量更好数据科学的项目。更好的质量可能是更少的错误、可靠的结果和更高的编码效率。
最佳实践都是从错误中总结出来的,所以这里我们总结了一些遇到的最常见的错误,并提供了如何最好地解决这些错误的方法、想法和资源。
1、不使用虚拟环境
这本身不是编码问题,但我仍然认为每种类型的项目进行环境的隔离是一个非常好的实践。
为什么要为每个项目使用专用环境呢?
第一个原因是Python
本身包管理的问题,我们想尽量减少包和版本之间的冲突。
另外一个原因是我们代码和依赖可以方便的部署到任意的位置
使用虚拟环境可以从Anaconda
或Pipenv
开始。如果想更深入那么Docker
是首选。
2、过度使用Jupyter Notebook
Notebooks
非常适合用于教育目的和做一些快速而复杂的分析工作,但它不能作为一个好的IDE
。
一个好的IDE
是应对数据科学任务时的真正武器,可以极大地提高您的工作效率。
Notebooks
很适合做实验,而且可以轻松地将结果展示给其他人。但是它很容易出错,当涉及到执行长期、协作和可部署的项目时,最好还是使用IDE
,例如VScode
、Pycharm
、Spyder
等。
3、使用绝对而不是相对路径
绝对路径的最大问题是无法进行方便部署,解决这个问题的主要方法是将工作目录设置为项目根目录,并且不要再项目中包含项目目录外的文件,并且在代码中的所有路径均使用相对路径。
代码语言:javascript复制import pandas as pd
import numpy as np
import os
#### 错误的方式 #####
excel_path1 = "C:\Users\abdelilah\Desktop\mysheet1.xlsx"
excel_path2 = "C:\Users\abdelilah\Desktop\mysheet2.xlsx"
mydf1 = pd.read_excel(excel_path1)
mydf2 = pd.read_excel(excel_path2)
#### 正确的方式 ####
DATA_DIR = "data"
#将要读取的文件复制到data目录
crime06_filename = "CrimeOneYearofData_2006.xlsx"
crime07_filename = "CrimeOneYearofData_2007.xlsx"
crime06_df = pd.read_excel(os.path.join(DATA_DIR, crime06_filename))
crime07_df = pd.read_excel(os.path.join(DATA_DIR, crime07_filename))
4、不处理警告
当我们的代码能够运行但产生奇怪的警告消息,我们很高兴终于让代码运行并收到了有意义的输出。但是我们需要处理这些警告吗?
首先,警告本身并不是错误,但它们是会引起我们对潜在错误或问题的提示。当你的代码中能够运行成功但可能不是它的预期方式时,警告就会出现。
我遇到的最常见的警告是Pandas
的SettingwithCopyWarning
和DeprecationWarning
。
SettingwithCopyWarning
最大的原因是Pandas
检测到链式赋值(Chained Assignment
)时发生的警告,我们应该避免对链式索引的结果赋值,因为这个操作有可能会报warning也有可能不会报。
DeprecationWarning
通常指出Pandas
弃用了某些功能,并且您的代码在使用更高版本时会中断。
这里的建议并不是要处理所有的警告,但是一定要对所有警告产生的原因有所了解,要知道在特定项目中那些警告式可以忽略的,那些警告的出现对结果会有影响,应当避免。
5、没有使用(很少使用)列表推导式
列表推导式是Python
的一个非常强大的特性。许多for
循环可以用更易读、更Python
且速度更快的列表推导来代替。
可以在下面看到一个示例代码,该代码旨在读取目录中的CSV
文件。可以看到,在使用列表推导时添很容易维护。
import pandas as pd
import os
DATA_PATH = "data"
filename_list = os.listdir(DATA_PATH)
#### 不好的方法 #####
csv_list = []
for fileaname in filename_list:
csv_list.append(pd.read_csv(os.path.join(DATA_PATH, filename)))
#### 建议 ####
csv_list = [pd.read_csv(os.path.join(DATA_PATH, filename)) for filename in filename_list]
list comprehensions
csv_list = [pd.read_csv(os.path.join(DATA_PATH,
filename)) for filename in filename_list if
filename.endswith(".csv")]
6、不适用类型注释
类型注释(或类型提示)是为变量分配类型的方法。在IDE
进行智能感知的提示时可以为我们提供指示变量/参数的类型。这不仅可以提高我们开发的速度,也可以对我们阅读代码有很大的帮助
def mystery_combine(a, b, times):
return (a b) * times
如果这么写,我们根本不知道a
,b
和times
的类型
def mystery_combine(a: str, b: str, times: int) -> str:
return (a b) * times
但是加上了类型注释,我们就知道a
和b
是字符串times
是整数
需要说明的是:Python
在3.5版本的时候引入了类型注释,Python
并不会在执行时检查类型注释,他只是为IDE
提供了一个方便静态类型检查工具,对动态语言做静态类型检查,来避免一些潜在的错误。
7、Pandas代码不规范
方法链是Pandas
的一个很棒的特性,但是如果在一行中包含了很多的操作,代码可能会变得不可读。
有一个技巧可以让这种方式边的简单,将表达式放入括号中,则可以对表达式的每个组件使用一行。
代码语言:javascript复制var_list = ["clicks", "time_spent"]
var_list_Q = [varname "_Q" for varname in var_list]
# 不可读的方法
df_Q = df.groupby("id").rolling(window=3, min_periods=1, on="yearmonth")[var_list].mean().reset_index().rename(columns=dict(zip(var_list, var_list_Q)))
# 可读性强的方法
df_Q = (
df
.groupby("id")
.rolling(window=3, min_periods=1, on="yearmonth")[var_list]
.mean()
.reset_index()
.rename(columns=dict(zip(var_list, var_list_Q))))
8、不遵守 PEP 约定
刚开始使用Python
进行编程时,代码可能是简陋并且不可读的,这是因为我们并没有自己的设计规则来让我的代码看起来更好。如果我们自己来设计这种规则是费事费力的并且这种规则需要很多的实践,好在Python
官方有已经指定好的规则:PEP
,它是Python
的官方样式指南。
虽然PEP
的规则很多并且很繁琐,我们可以忽略了一些PEP
规则,但可以在90% 的代码中使用了它们。
9、不适用编码辅助工具
您想在编码方面大幅提高生产力吗?请开始使用编码辅助工具,它通过巧妙的自动完成、打开文档和提供改进代码的建议来提供帮助。
pylance
、Kite
、tabnine
、copilot
都是非常好的选择。