在编写代码时,如果不小心,研究人员可能会遇到代码可读性、可重复性和生产率方面的问题。我每天都用Python编写代码,通过大量的试验和错误,我学到了一些重要的“做”和“不做”。其中一些技巧也与语言无关,因此即使 Python 不是您的首选,也请继续阅读。
1.了解基础数据科学库
许多人没有意识到,有一些经过时间考验的库,它们允许您加载数据集,分析它们的描述性统计数据,应用过滤器,并在几行代码中生成有意义的可视化结果。以下内容特别有用:
- Pandas:数据集加载和操作
- Numpy和Scipy:矢量运算、数学/科学函数
- Scikit-Learn:统计数据和经典机器学习算法
- Matplotlib或Seaborn或Ploty:可视化/绘图
2.掌握命令行
无论您是连接远程集群、导航自己的文件系统还是运行脚本,在某些时候您都可能需要与命令行进行交互。您应该了解基本的 Linux shell
命令,例如 ls
(列出目录中的文件)、cd
(更改目录)、mkdir
(制作目录)、rm
(删除文件)、wc
(计算行数、字数和字节数)、mv
(移动文件)、cp
(复制文件)、grep
(快速搜索文件中的模式)、head
(查看文件的第一个块)和 ssh
(安全连接到远程设备)。了解一些更小众的命令也很有帮助,例如 ln
制作符号链接和 scp
将文件传输到/从远程服务器传输。请注意,这些命令在 Mac OS
和 Linux
上是相同的。没有必要记住每一个命令(虽然如果你忘记了一个命令的作用,man
命令是非常有用的),但上面提到的那些真的可以走很长的路。了解如何制作一个简单的 shell
脚本来一次运行一堆任务,以及如何使用 cron
来安排作业也很有帮助。
3.学习 git 和 GitHub
在很长一段时间里,我对 git
有一个表面层次的了解,但从未真正深入挖掘过。我使用 GitHub
上传和与合作者共享代码文件,但我将其视为带有语法突出显示的 Google Drive
。在编码实习期间,我真的对 git
有了更好的了解,这极大地改善了我的工作流程。 在我的研究中,我经常需要使用 ssh
在远程服务器上执行长时间运行或计算密集型的作业。然而,我也很享受在我的个人机器上直接编码的体验。使用 git
和 GitHub
,我只需按几下键就可以使我的个人计算机与远程服务器保持同步。我总是可以回溯到我之前提交的代码版本,所以我不必担心不小心删除或弄乱了我的代码。另一个有用的工作流程是创建一个功能分支来尝试一个想法,只有在想法成功时才将其合并回主分支。
请记住,git
中有很多功能,在您掌握它之前可能需要一段时间。对自己要有耐心,记住最好的学习方式是实践,而不是被动地观看。重点学习以下git
命令的含义和用法:git init、git clone、git add、git commit、git push、git pull、git branch、git checkout、git merge。
除了提高您自己的生产力,git
还使协作变得更加容易。协作者可以克隆您的存储库、进行更改、将它们推送到新分支,并为您打开拉取请求以查看这些更改。本着开放科学和可重复性的精神,如今很多已发表的论文只会包含一个指向带有研究人员代码的 GitHub
存储库的链接,如果你使用GitHub
来跟踪你自己的工作,那么当你需要与更广泛的科学界分享你的工作时,这个步骤的大部分已经为你完成了。
4.使用代码格式化程序
什么是代码格式化程序?它是一个自动格式化代码的程序,处理从逗号后的空格、换行符到易于阅读的数据结构(如 Python 字典和 JSON)显示的所有内容。你可以花更少的精力去担心你应该在一行中放置多少个列表项,而花更多的精力去实际解决问题和思考你的代码。
我推荐black。我在我的 IDE (Visual Studio Code) 中设置了black
以在每次保存时自动格式化我的代码。下面的示例显示了一些 Python 代码在应用自动格式化之前和之后的样子。请注意,代码格式化程序会将长行分解为更具可读性的块。有些人可能不喜欢这样,特别是如果他们真的喜欢将大量代码压缩到一行中。但是自动格式化程序使代码对其他人和未来的自己更具可读性。自动格式化程序消除了编码体验中不必要的自由度,允许使用不同方法的编码人员共享更一致的风格。 git
中的更改将更有可能反映实际内容而不是风格。
# without formatter
dictionary = {"a":[1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1], "b":[9,8,7,6,5,4,3,2,1,2,3,4,5,6,7,8,9]}
list_of_items = [f"A: {a}, B: {b}, C: {c}" for a, b, c in itertools.product(range(0,100,2), range(0,100,3), range(0,100,4))]
# with formatter
dictionary = {
"a": [1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1],
"b": [9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9],
}
list_of_items = [
f"A: {a}, B: {b}, C: {c}"
for a, b, c in itertools.product(
range(0, 100, 2), range(0, 100, 3), range(0, 100, 4)
)
]
5.组织和标准化您的文件系统
这似乎是一个相当微不足道的建议,但在一个长期项目中,跟踪不同的数据集、输出文件、可视化、脚本、Jupyter 笔记本等确实会变得很困难。
为您的目录和文件夹使用标准化的命名约定。您可以使用 Python
的 datetime
库获取当前日期以自动向程序输出文件(例如日志或图像)添加时间戳。这可以避免让您的程序覆盖具有相同名称的先前输出。尝试将数据保存在一个文件夹中,将源文件保存在另一个文件夹中,将图文件(如可视化和绘图)保存在另一个文件夹中。
from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d")
filename = f"outputfile-{timestamp}.txt"
将数据文件加载到程序中时,您可以使用 glob
库轻松检索匹配特定字符串模式的文件,并且可以使用 os.path.exists()
来检查文件是否存在。
import glob
# returns a list of all files matching the pattern
list_of_files = glob.glob("outputfile-2021-10-*.txt")
6.使用notebook探索实验程序
首先,我喜欢 Jupyter Lab
和 Jupyter notebook
。它们是加载数据集、执行清理和探索性数据分析 (EDA) 以及测试代码片段和功能的便捷方式。也就是说,所有事情都有一个时间和地点,我在使用 Jupyter 处理所有事情时遇到了一些问题。
首先,Jupyter notebook
的输出将取决于您运行单元格的顺序。这可能会导致变量没有您期望的值的问题。 Jupyter notebooks
也可能有点难以共享,如果你试图在输出单元的海洋中找到某段代码,那么长的 notebook
可能会导致可读性噩梦。
一旦您在notebook
中完成了探索,我建议将notebook
中的代码重构成一个完整的程序。使用if __name__ == " main ":
语法来运行代码,并将大多数代码分解为内聚函数。考虑留出时间,也许每周一次,当您查看所有的笔记本和各种脚本时,您将它们重构为一个编写良好、评论良好、具有凝聚力的程序。
7.使用命令行参数增强再现性
做研究通常需要在这里和那里调整模型,直到你得到有趣的结果。我过去这样做的方法是编辑代码文件并直接在代码中更改参数并再次运行它。不幸的是,经常发生的情况是,您使用一系列不同的设置运行代码,其中一个输出很有趣,但您不记得为了获得该输出所做的调整。
相反,通常最好将任何运行时参数分解为命令行参数,然后创建使用不同参数运行相同代码的 shell
脚本。我使用 argparse Python
库来做到这一点。该库可以轻松设置命令行参数,包括指定默认值、数据类型和标志参数。下面的代码片段显示了一个简单的程序示例,该程序读取 3 个命令行参数并使用它们读取文件并可选择打印一些行。
import argparse
import pandas as pd
if __name__ == "__main__":
# create argument parser and define arguments
parser = argparse.ArgumentParser()
parser.add_argument("--inputfile", default="inputfile.csv")
parser.add_argument("--num_rows", type=int, default=10)
parser.add_argument("--print_output", action="store_true")
args = parser.parse_args()
# read in dataset
df = pd.read_csv(args.inputfile)
df_subset = df.iloc[:args.num_rows, :]
# print dataset if flag is set
if args.print_output:
print(df_subset)
给定上面的程序,您可以创建如下所示的 shell
脚本,以使用某些参数运行它:
#!/bin/bash
python argparse_example.py --inputfile datafile.csv --num_rows 100 --print_output
这样可以更轻松地记录您的工作,让您准确了解您运行的内容以获得各种结果。实际代码可以保持不变,并且您可以轻松地为您想要尝试的每组参数在 shell 脚本中添加一个新行。
8.为整个项目创建一个具有有用功能的实用程序类
您的项目最终可能会有许多不同的程序,包括实验脚本、数据清理脚本、可视化脚本等等。许多这些程序可能会重复使用相同的代码。我喜欢创建一个 utils.py
类,它可以访问我的整个项目并将共享函数放在那里。例如,如果在我的整个项目中使用了名为 load_dataset()
和 clean_dataset()
的函数,我可以将它们放在 utils.py
中,其他程序可以通过以下方式调用它们:
import utils
data = utils.load_dataset("data.csv")
data = utils.clean_dataset(data)
显然,这不是分解共享代码的唯一方法,并且可能不是最适合您的用例的方法。但是在很多中小型研究项目中,这可能足以让你省去很多头痛。数据集处理、预处理或绘图等重复任务可以抽象到单个文件中,以便于进行更改。您绝对不想做的是在多个文件中复制相同的代码,然后在更新一个版本但忘记更新另一个版本时遇到神秘的不一致。
总结
我希望你喜欢这些建议!在研究中,完成工作的一个重要部分是优化你的工作流程,消除生产力的障碍。学习这些技能需要时间,但从长远来看可以为你节省更多的时间和精力,对你的学术生涯肯定有帮助。
参考目录
https://towardsdatascience.com/7-essential-python-skills-for-research-496e1888e7c2