美文网首页脑核磁共振数据处理——fMRI
Nipype学习笔记(2)——Input & Outpu

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

作者: 韧心222 | 来源:发表于2018-06-26 20:42 被阅读144次

    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() 
    

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

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

    未完待续

    相关文章

      网友评论

        本文标题:Nipype学习笔记(2)——Input & Outpu

        本文链接:https://www.haomeiwen.com/subject/bocuyftx.html