Nipype学习笔记(2)——Input & Output

4. 数据的输入

在Nipype中提供了丰富的文件操作功能,使得我们在数据处理时不需要再把数据“复制过来,粘贴过去”,不仅极大地简化了操作,也避免了存储空间的浪费。

4.1 DataGrabber

顾名思义,DataGrabber是一个数据抓取工具,能够从指定的目录中按照一定的规则挑选出需要的数据。

假设我们使用的是DS00014的数据集合,其数据结构如下图所示:


image.png

对于这样一个数据集合,如何从中挑选出fingerfootlips任务的数据呢?其实可以这样:

import nipype.interfaces.io as nio
# 生成一个DataGrabber对象
ds = nio.DataGrabber()
# 指定根目录
ds.inputs.base_directory = '/data/ds000114'
# 指定模板,用来过滤文档
ds.inputs.template = 's*/ses-test/func/*fingerfootlips*.nii.gz'
# 需要注意的是,此处并没有对文档进行排序,如果需要排序需要指定sorted参数
ds.inputs.sort_filelist = True
# 执行
results = ds.run()
# 查看执行结果
results.outputs

使用这段代码,可以在/data/ds000114文件夹下挑选出所有满足s*/ses-test/func/*fingerfootlips*.nii.gz这一模式的文档。如果想要过滤出特定ID的被试怎么办呢?可以这样:

# 在生成DataGrabber对象的时候,需要指定infields参数
ds = nio.DataGrabber(infields=['subject_id'])
ds.inputs.base_directory = '/data/ds000114'
# 使用%02d来设计模板
ds.inputs.template = 'sub-%02d/ses-test/func/*fingerfootlips*.nii.gz'
ds.inputs.sort_filelist = True
# 指定subject_id的具体数值
ds.inputs.subject_id = [1, 7]
results = ds.run()
results.outputs

可见,Nipype的数据挑选方式有多么灵活。

官方文档中还给了几个其他的例子,其实并不是很好懂,建议没有时间的小伙伴们不要把太多的精力放在上面,因为接下来要介绍的SelectFiles会比DataGrabber更加的灵活和清晰。唯一还需要说明的是,DataGrabber的构造函数中,还有一个常用的参数,叫outfields,这个参数的含义是DataGrabber的输出会变成一个类似于字典的数据结构,该结构的KEY是由outfileds来确定。使用outfileds后,就可以用同一个DataGrabber使用不同的模板来挑选各种各样的文件了。

4.2 SelectFiles

相比于DataGrabber,SelectFiles应该是一种更加直观的数据抓取工具,它使用的是基于大括号{}的模板。这对于写过Rails和Flask的小伙伴来说应该会更加容易理解。先举个例子(这也是官方文档的例子):

msg = "This workflow uses {package}."
print(msg.format(package="FSL"))

这样一段Python代码执行之后,会看到什么结果呢?运行之后,我们可以看到:

This workflow uses FSL.

很直观的,源代码中的{package}变成了FSL,这也就是我所谓的基于大括号的模板,那么在实际中应该如何应用呢?再来看下一段代码:

from nipype import SelectFiles, Node

templates = {'anat': 'sub-{subject_id}\\ses-{ses_name}\\anat\\sub-{subject_id}_ses-{ses_name}_T1w.nii.gz',
             'func': 'sub-{subject_id}\\ses-{ses_name}\\func\\sub-{subject_id}_ses-{ses_name}_task-{task_name}_bold.nii.gz'}

# Create SelectFiles node
sf = Node(SelectFiles(templates),
          name='selectfiles')

# Location of the dataset folder
sf.inputs.base_directory = 'E:\\ds114_R2.0.0'

# Feed {}-based placeholder strings with values
sf.inputs.subject_id = '0[1,2]'
sf.inputs.ses_name = "test"
sf.inputs.task_name = 'fingerfootlips'
sf.run().outputs

运行上述代码之后,会得到如下结果:

anat = ['E:\\ds114_R2.0.0\\sub-01\\ses-test\\anat\\sub-01_ses-test_T1w.nii.gz', 'E:\\ds114_R2.0.0\\sub-02\\ses-test\\anat\\sub-02_ses-test_T1w.nii.gz']
func = ['E:\\ds114_R2.0.0\\sub-01\\ses-test\\func\\sub-01_ses-test_task-fingerfootlips_bold.nii.gz', 'E:\\ds114_R2.0.0\\sub-02\\ses-test\\func\\sub-02_ses-test_task-fingerfootlips_bold.nii.gz']

首先,可以看到,按照模板的设置,系统的输出会分成anatfunc两部分,这个的效果就与DataGrabber的outfields是一致的。其次模板中的{subject_id}被替换成了01和02,{ses_name}被替换成了test,{task_name}被替换成了fingerfootlips。当然,和DataGrabber一样,模板中还是可以使用*符号的。最后,在替换时,使用的是字符串直接替换,因此在给模板中的变量赋值的时候,是不能使用['01','02']这种形式的,否则系统会报错。

5. BIDS格式的输入

DataGrabber和SelectFiles为我们提供了便捷的数据选择工具,不过如果你使用的是标准的BIDS格式的数据(据说OpenfMRI上的数据都是以这种格式组织的),那么恭喜你,Nipype提供了更加便捷的数据访问方式。

5.1 pybids

要想享受Nipype和BIDS给你带来的种种便捷,还需要安装pybids这样一个python工具包,安装的方式很简单:

pip install pybids

我自己刚刚试了一下,想用conda安装,不过报错了,可能需要添加新的channel,回头再研究一下这个问题。用pip安装是没有任何问题的。

在使用pybids的时候,先导入BIDSLayout对象,在生成一个对象(好吧,其实你注意到了,我把数据换了路径存放,而且是在windows下)

from bids.grabbids import BIDSLayout
layout = BIDSLayout("E:\\Workspaces\\nipy\\data")

之后就可以使用layout来获得一些数据集的元信息以及一些具体的任务对应的数据文件。

先来看一下一些常用的元数据的获取方法,包括:

  • get_subjects() 获取所有的被试
  • get_modalities()
  • get_types()
  • get_tasks() 获取所有的任务
  • get_metadata 获取nii和nii.gz文件的元数据

如果要获得对应的数据文件,就可以直接调用get()函数,并在get函数中通过参数指明所需要的数据集的特征,常用的参数包括:

  • subject 指定被试,可以指定多个被试,例如:layout.get(subject=['01','02'])就可以获取所有01和02的数据文件
  • modality
  • type
  • session
  • task 指定执行的任务,例如:layout.get(task='')就可以获得
  • return_type 返回数据的类型。默认情况下,get函数会返回一个文件对象,包括该文件所属的被试、session、task、type等信息,例如:File(filename='E:\\Workspaces\\nipy\\data\\sub-02\\ses-test\\func\\sub-02_ses-test_task-linebisection_events.tsv', subject='02', session='test', task='linebisection', type='events', modality='func'),如果想要获得的只是文件路径,那么就需要指定该参数为'file'
  • extensions 指定返回文件的扩展文件名,例如extensions=['nii', 'nii.gz'],就会返回所有后缀为nii和nii.gz的文件

5.2 pybids+nipype

pybids 已经很好的完成了所有想要做的工作,唯一的缺陷是还不能加入到我们的Workflow中,那么该如何实现呢?一个比较直观的想法就是将pybids的工作封装到Node中,而封装就需要先定义一个函数:

def get_niftis(subject_id, data_dir):
    from bids.grabbids import BIDSLayout
    layout = BIDSLayout(data_dir)
    bolds = layout.get(subject=subject_id, type="bold", return_type='file', extensions=['nii', 'nii.gz'])
    return bolds

之后,就需要定义自定义Node节点了

from nipype.pipeline import Node
from nipype.interfaces.utility import Function
BIDSDataGrabber = Node(Function(function=get_niftis, input_names=["subject_id", "data_dir"], output_names=["bolds"]), 
                       name="BIDSDataGrabber")

之后,就是输入

BIDSDataGrabber.inputs.data_dir = "E:\\Workspaces\\nipy\\data"
BIDSDataGrabber.inputs.subject_id='01'
res = BIDSDataGrabber.run()
res.outputs

但是,这样是无法遍历所有的subjects,如果需要遍历,还需要指定iterables参数(此处没有想明白,需要在实际操作的时候再来完善,先把这部分的内容记录下来)

BIDSDataGrabber.iterables = ('subject_id', layout.get_subjects()[:2])

6. 数据的输出

在Nipype中,输出也是以模块的形式呈现的,主要包括以下几种输出模块:

  • DataSink
  • JSONFileSink
  • MySQLSink
  • SQLiteSink
  • XNATSink

从名称上不难发现,其中绝大多数是和特定的数据工具相关,比如MySQLSink和SQLiteSink一定是与MySQL和SQLite两种数据库相关,XNATSink肯定是和XNAT数据相关,JSONFileSink应该是以JSON文件的格式来存储和管理数据,DataSink应该是一种更为通用的数据格式。因此,不妨从DataSink来开始我们的学习。

XNAT( Extensible Neuroimaging Archive Toolkit )是一个开源的软件平台,旨在帮助神经影像及其相关数据的管理和挖掘。

https://www.xnat.org

6.1 DataSink

如前所述,DataSink是一种较为通用的数据存储模块,通常用于存储结构化的输出。在Workflow中,其工作目录就如同一个缓存,会存储着各个阶段的计算结果(如日志信息等),如果直接浏览Workflow的工作目录会让人十分崩溃,而我们的目标通常是这些中间过程中的一小部分。DataSink可以将缓存中的结果提取出来并放置到不同的位置。

DataSink中第一个重要参数是base_directory,该参数指定了DataSink存储的根目录。其他输入参数用于和其他模块对接,有趣的是DataSink用这些输入参数的名称指定了目录结构。其基本形式如下

string[[.[@]]string[[.[@]]string]] …

其中[]表示其中的内容是可选项,在DataSink中用.表示目录结构关系,例如:contrasts.con表示在根目录下会创建contrasts/con这样的目录结构,如果带有@则表示不会建立新的目录,例如contrasts.@con,则会创建contrasts目录,并把结果存储在contrasts目录下。

如果觉得说的不清楚,还是来看个例子吧,先来看一下Workflow图:


Workflow

代码如下:

datasink = pe.Node(nio.DataSink(), name='sinker')
datasink.inputs.base_directory = '/path/to/output'
workflow.connect(inputnode, 'subject_id', datasink, 'container')

我们先创建了一个DataSink节点,然后将其根目录指定为/path/to/output,然后将inputnode节点的输出subject_id与datasink的container相连接,因此就会在/path/to/output/container中存储InputNode的subject_id输出。

workflow.connect(realigner, 'realigned_files', datasink, 'motion')
workflow.connect(realigner, 'realignment_parameters', datasink, 'motion.@par')

执行上面语句后,就会把头动矫正后的数据文件存储到/path/to/output/container/motion文件夹下,并将头动参数也存储到该文件夹下。

6.2 MySQLSink

MySQLSink用于将一些值直接存储到MySQL数据库中,示例代码如下:

sql = MySQLSink(input_names=['subject_id', 'some_measurement'])
sql.inputs.database_name = 'my_database'
sql.inputs.table_name = 'experiment_results'
sql.inputs.username = 'root'
sql.inputs.password = 'secret'
sql.inputs.subject_id = 's1'
sql.inputs.some_measurement = 11.4 
sql.run() 

这对于用过数据库的同学而言应该十分简单,就不做过多的说明了。

大致看了一下,其他几种模块都大同小异,就不仔细学习了,如果需要的话再去看文档吧,目前应该用不到。

未完待续

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,894评论 2 89
  • 1、 性能调优 1.1、 分配更多资源 1.1.1、分配哪些资源? Executor的数量 每个Executor所...
    Frank_8942阅读 4,524评论 2 36
  • 停下飞舞的笔触,忘却常曰里名与利的争吵,抛弃心中的杂念,择一个清爽的午后,泡一盏清茶,倚一方长凳,看着屋檐下筑巢的...
    尹韵阅读 404评论 2 3
  • 库依位于新疆阿勒泰地区阿尔泰山脉中,从富蕴县大桥边防站往西北方向走80多公里,或者从库依边防站往东北走50...
    tangpeiliang阅读 374评论 0 2