我尝试使用GNU make来组织Data Science CookieCutter project推荐的研究数据,处理和可视化。我的原始数据结构如下:
.
├── data
│ ├── interim
│ │ └── cleaned
│ └── raw
│ ├── ex01
│ └── ex02
我将实验1和2的数据分开,但在清洗后将它们合并。例如data/raw/ex01/p0-c0.csv
成为data/interim/cleaned/ex01-p0-c0.hdf
。
在make中我使用两个这样的规则:
data/interim/cleaned/ex01-%.hdf: data/raw/ex01/source0/%.csv
data/raw/ex01/source1/%.csv
$(PYTHON) src/data/make_dataset.py $^ $@
data_interim_cleaned_ex01: $(addprefix $(CLEANED_DIR)/ex01-, $(addsuffix .hdf, $(basename $(basename $(notdir $(wildcard data/raw/ex01/source0/*.csv))))))
这让我感到奇怪的冗长(特别是因为我复制了实验2的块)而我的直觉告诉我,如果有多个(命名的)通配符会更容易。我猜regexps会有所帮助,但不是(容易)在make中可用。
是否有规范的方法来解决这个问题?
以下解决方案并不是真正的规范make
文件,但恕我直言,许多制作的规范功能很难掌握和记住无论如何。诸如“如何将我的文件名从形状X转换为Y”这样的问题一直存在,因为用户确实使用目录和文件名结构作为组织项目的手段(一种非常自然和合乎逻辑的方式),而make
装备非常糟糕以编程方式处理此类任务。
一种方法是使用常见的命令行工具,如sed
,另外一种是帮助库,如gmtt来拆分字符串:
include gmtt-master/gmtt.mk
COMMON_ROOT = data/raw
COMMON_DEST = data/interim/cleaned
SOURCE = data/raw/ex01/p0-c0.csv data/raw/ex01/p1-c1.csv data/raw/ex02/p0-c0.csv data/raw/ex02/p1-c1.csv
# a pattern which separates a string into 5 parts (see below)
SEP_PATTERN = $(COMMON_ROOT)/ex*/*.csv
# use the elements (quoted variable-references '$$'!) in the new filename
OUTPUT_PATTERN = $(COMMON_DEST)/ex$$2-$$4.hdf
# glob-match tests a glob pattern on a string and returns the string cut up at the border of
# the glob elements (*,?,[] and verbatim strings). We immediately turn this into a gmtt table
# by prepending the number of columns (5) to it:
SEPARATED = 5 $(foreach fname,$(SOURCE),$(call glob-match,$(fname),$(SEP_PATTERN)))
$(info $(SEPARATED))
$(info -----------------)
$(info $(call map-tbl,$(SEPARATED),$(OUTPUT_PATTERN)$$(newline)))
输出:
$ make
5 data/raw/ex 01 / p0-c0 .csv data/raw/ex 01 / p1-c1 .csv data/raw/ex 02 / p0-c0 .csv data/raw/ex 02 / p1-c1 .csv
-----------------
data/interim/cleaned/ex01-p0-c0.hdf
data/interim/cleaned/ex01-p1-c1.hdf
data/interim/cleaned/ex02-p0-c0.hdf
data/interim/cleaned/ex02-p1-c1.hdf
make: *** Keine Ziele. Schluss.
我担心将makefile转换为动态生成规则的makefile是不可避免的。
答案可能不是你想要的,但不是在文件名中引入可变性或重复。在添加或删除前缀(例如目录名称)或后缀的词干名称之间,可以使用简单或至少合理的方式表达Makefile
中的关系。其他任何事情都会造成复杂的问题,你最终会受到折磨和复杂的转换规则或外部帮助脚本来管理映射,或者在最糟糕的情况下,你只需要放弃make
进行依赖管理。
一种允许你保留蛋糕并吃掉它的解决方法是在首选的,人性化的命名约定和make
管理的结构之间建立符号链接;但这充其量只是一个拐杖。
另一种可能对您有用的技术是touch
一个简单的标志文件,用于标记一组复杂的依赖关系。特别是如果存在不直接映射到另一个目标的一组输入文件名的依赖项,那么将所有这些都放在一个简单的后面
.input-files-done: some complex depencies
touch $@
然后只是依靠.input-files-done
为共享这些依赖关系的目标可以简化你的Makefile
和你的工作流程。
但总的来说,我的主要建议是保持文件名统一,以便您始终可以使用简单的规则声明从一个文件名到另一个文件名的显式依赖关系。