微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

xarray expand_dim添加更高级别的维度 设置功能定义 xarray部分

如何解决xarray expand_dim添加更高级别的维度 设置功能定义 xarray部分

我正在尝试合并一个数据数组列表,然后添加一个维,以便对每个串联的数据数组进行标记。我以为这是expand_dims的用例,但是尝试了SO的各种解决方案后,我陷入了困境。我认为我缺少有关xarray的基本知识。 这些似乎最接近:

  1. Add a 'time' dimension to xarray Dataset and assign coordinates from another Dataset to it

  2. Add 'constant' dimension to xarray Dataset

im使用pandas数据框从文件名中编译元数据,然后分组并遍历组以创建数据集,使用skimage.io.ImageCollection将多个图像文件加载到nparray中,并最终创建xarray对象

独立示例

设置

#%%  load libraries
from itertools import product
from PIL import Image
import numpy as np
import pandas as pd
import xarray as xr
import glob
from skimage import io
import re

#%% Synthetic data generator
ext = 'png'
delim = '_'

datadir = os.path.join('data','syn')
os.makedirs(datadir,exist_ok=True)
cartag = ['A1','A2']
date = ['2020-05-31','2020-06-01','2020-06-02']
frame = ['Fp','Fmp']
parameter = ['FvFm','t40','t60']
list_vals = [cartag,date,frame,parameter]
mesh = list(product(*list_vals))
mesh = np.array(mesh)
for entry in mesh:
    print(entry)
    img = np.random.random_sample((8,8))*255
    img = img.astype('uint8')
    fn = delim.join(entry)+'.png'
    pimg = Image.fromarray(img)
    pimg.save(os.path.join(datadir,fn))

#%% import synthetic images
fns = [
    fn for fn in glob.glob(pathname=os.path.join(datadir,'*%s' % ext))
]
flist = list()
for fullfn in fns:
    fn = os.path.basename(fullfn)
    fn,_ = os.path.splitext(fn)
    f = fn.split(delim)
    f.append(fullfn)
    flist.append(f)

fdf = pd.DataFrame(flist,columns=[
                    'plantbarcode','timestamp','frame','parameter','filename'
                ])
fdf=fdf.sort_values(['timestamp','plantbarcode','frame'])

功能定义

#%%
def get_tind_seconds(parameter):
    tind = re.search("\d+",parameter)
    if tind is not None:
        tind = int(tind.group())
    elif parameter == 'FvFm':
        tind = 0
    else:
        raise ValueError("the parameter '%s' is not supported" % parameter)
    return (tind)

xarray部分

dfgrps = fdf.groupby(['plantbarcode','parameter'])
ds = list()
for grp,grpdf in dfgrps:
    # print(grpdf.parameter.unique())
    parameter = grpdf.parameter.unique()[0]
    tind = get_tind_seconds(
        parameter
    )  #tind is an integer representing seconds since start of experiment
    # print(tind)

    filenames = grpdf.filename.to_list()
    imgcol = io.ImageCollection(filenames)
    imgstack = imgcol.concatenate()  #imgstack is Now 2x8x8 ndarray
    indf = grpdf.frame  #the 2 dim are frames Fp and Fmp
    # print(indf)
    arr = xr.DataArray(name=parameter,data=imgstack,dims=('frame','y','x'),coords={
                    #        'frame': indf,'parameter': [parameter,parameter]
                    #        'tind_s': [tind,tind]
                       },attrs={
                           'jobdate': grpdf.timestamp.unique()[0],'plantbarcode': grpdf.plantbarcode.unique()[0]
                       })
    # arr = arr.expand_dims(
    #     dims={'tind_s': tind}
    # )  #<- somehow I need to label each dataarray with another dimension assigning it the dim/coord `tind`
    ds.append(arr)

dstest = xr.concat(ds,dim='parameter')

目标是每天有一个不同的文件,即植物条形码。因此,在这种情况下为4个文件其中的图像可通过参数和帧索引。 tind_s通常可用于为每个参数绘制每个图像的摘要统计信息,因此我也想使该图像变暗/协调-我不确定何时使用它。看起来昏暗必须匹配输入的数据,因此在这种情况下为2帧x 8x8像素。

原始

使用熊猫数据框从文件名(这里是前几项)中编译元数据

    frameid plantbarcode    experiment  datetime    jobdate cameralabel filename    frame   parameter
4   5   A1  doi 2020-05-31 21:01:55 2020-06-01  PSII0   data/psII/A1-doi-20200531T210155-PSII0-5.png    Fp  FvFm
5   6   A1  doi 2020-05-31 21:01:55 2020-06-01  PSII0   data/psII/A1-doi-20200531T210155-PSII0-6.png    Fmp FvFm
6   7   A1  doi 2020-05-31 21:01:55 2020-06-01  PSII0   data/psII/A1-doi-20200531T210155-PSII0-7.png    Fp  t40_ALon
7   8   A1  doi 2020-05-31 21:01:55 2020-06-01  PSII0   data/psII/A1-doi-20200531T210155-PSII0-8.png    Fmp t40_ALon
8   9   A1  doi 2020-05-31 21:01:55 2020-06-01  PSII0   data/psII/A1-doi-20200531T210155-PSII0-9.png    Fp  t60_ALon
9   10  A1  doi 2020-05-31 21:01:55 2020-06-01  PSII0   data/psII/A1-doi-20200531T210155-PSII0-10.png   Fmp t60_ALon
...

然后分组并遍历各个组以创建数据集,使用skimage.io.ImageCollection将多个图像文件加载到nparray中,并最终创建xarray对象

import os
import cppcpyutils as cppc
import re
from skimage import io
import xarray as xr
import numpy as np
import pandas as pd

delimiter = "(.{2})-(.+)-(\d{8}T\d{6})-(.+)-(\d+)"

filedf = cppc.io.import_snapshots('data/psII',camera='psII',delimiter=delimiter)
filedf = filedf.reset_index().set_index('frameid')

pimframes_map = pd.read_csv('data/pimframes_map.csv',index_col = 'frameid')

filedf = filedf.join(pimframes_map,on = 'frameid').reset_index().query('frameid not in [3,4,5,6]')
dfgrps = filedf.groupby(['experiment','jobdate','datetime','parameter'])

ds=list()
for grp,grpdf in dfgrps:
    # print(grpdf.parameter.unique())
    parameter = grpdf.parameter.unique()[0]
    tind = get_tind_seconds(parameter) #tind is an integer representing seconds since start of experiment
    # print(tind)

    filenames = grpdf.filename.to_list()
    imgcol = io.ImageCollection(filenames)
    imgstack = imgcol.concatenate() #imgstack is Now 2x640x480 ndarray
    indf = grpdf.frame #the 2 dim are frames Fp and Fmp
    # print(indf)
    arr = xr.DataArray(name=parameter,dims=('induction frame',coords={'induction frame': indf},attrs={'plantbarcode': grpdf.plantbarcode.unique()[0],'jobdate': grpdf.jobdate.unique()[0]})
    arr = arr.expand_dims(dims = {'tind_s': tind}) #<- somehow I need to label each dataarray with another dimension assigning it the dim/coord `tind`
    ds.append(arr)

expand_dims行导致ValueError: dimensions ('dims',) must have the same length as the number of data dimensions,ndim=0

如果我尝试遵循上面链接的第二个SO,则在其中我提供“ tind_s”作为坐标,它会抱怨相对于暗角有太多。

ValueError: coordinate tind_s has dimensions ('tind_s',),but these are not a subset of the DataArray dimensions ('induction frame','x')

然后我想在tind_s是坐标的情况下合并在一起

dstest=xr.concat(ds[0:4],dim = 'tind_s')

另一种尝试

我确实弄清楚了可以在np.expand_dims()上使用imgstack,然后指定多余的dim和coord,但这会导致数组为nan。另外,xr.concat()的结果是数据数组而不是数据集,因此无法保存(?)。在xarray中有直接的方法可以做到这一点吗? 我也将属性转换为暗淡

dfgrps = filedf.groupby(
    ['experiment','parameter'])

dalist = list()
for grp,grpdf in dfgrps:
    print(grpdf.parameter.unique())
    parameter = grpdf.parameter.unique()[0]
    tind = get_tind_seconds(parameter)
    # print(tind)
    print(grpdf.plantbarcode.unique())
    print(grpdf.jobdate.unique()[0])

    filenames = grpdf.filename.to_list()
    imgcol = io.ImageCollection(filenames)
    imgstack = imgcol.concatenate()
    imgstack = np.expand_dims(imgstack,axis=0)
    imgstack = np.expand_dims(imgstack,axis=0)
    indf = grpdf.frame  #xr.Variable('induction frame',grpdf.frame)
    # tind = xr.Variable('tind',[tind])
    # print(indf)
    arr = xr.DataArray(data=imgstack,dims=('jobdate','tind_s','induction frame',coords={
                           'plantbarcode': grpdf.plantbarcode.unique(),'tind_s': [tind],'induction frame': indf,'jobdate': grpdf.jobdate.unique()}
    )
    dalist.append(arr)

ds = xr.concat(dalist,dim='jobdate')

for循环之后:print(arr)

<xarray.DataArray (jobdate: 1,plantbarcode: 1,tind_s: 1,induction frame: 2,y: 640,x: 480)>
array([[[[[[0,...,0],[1,1,[0,2,1],0]],[[0,[2,0]]]]]],dtype=uint8)
Coordinates:
  * plantbarcode     (plantbarcode) object 'A2'
  * tind_s           (tind_s) int64 60
  * induction frame  (induction frame) object 'Fp' 'Fmp'
  * jobdate          (jobdate) datetime64[ns] 2020-06-03
Dimensions without coordinates: y,x

print(ds)


print(ds)
<xarray.DataArray (jobdate: 18,plantbarcode: 2,tind_s: 3,x: 480)>
array([[[[[[ 0.,0.,1.],[ 0.,1.,2.,0.],[ 1.,7.,4.,4.],0.]],[[ 0.,1.]]],[[[nan,nan,nan],[nan,...
           [nan,nan]]],[[[ 0.,[ 2.,0.]]]]]])
Coordinates:
  * plantbarcode     (plantbarcode) object 'A1' 'A2'
  * tind_s           (tind_s) int64 0 40 60
  * induction frame  (induction frame) object 'Fp' 'Fmp'
  * jobdate          (jobdate) datetime64[ns] 2020-06-01 ... 2020-06-03
Dimensions without coordinates: y,x

我不知道nan数组的来源。对我来说也很奇怪,即使concat中使用了什么dim,每个条目都有一个coord值(在这种情况下为18个文件),即使它们不是唯一的,而其他dims显示为唯一值。

如果有人愿意在此处下载小型数据集,请使用link(很抱歉,链接中的建议,我会尝试提供可以即时生成的综合数据集)>

解决方法

您的原始代码在arr.expand_dims(dims={'tind_s': tind})中包含一个细微的错误(typo):我想您想要dim而不是dims,后者被xarray解释为新的尺寸标签(请参见doc)。另外,tind用作沿新维度创建的元素数量,这可能也不是您想要的。

您的其他尝试(即在创建DataArray之前扩展数据维度)是IMO的更好方法,但是可以进一步改进。假设您在同一级联维度上有多个标签,建议您创建一个多索引并将其分配给级联维度,即类似

import numpy as np
import pandas as pd
import xarray as xr


da_list = []
props = []
prop_names = ['experiment','plantbarcode','tind']

for i in range(10):
    tind = i
    indf = ['Fp','Fmp']
    data = np.ones((2,640,480)) * i
    
    da = xr.DataArray(
        data=data[None,...],dims=('props','frame','y','x'),coords={'frame': indf}
    )

    props.append((f'experiment{i}',i*2,i))
    da_list.append(da)


prop_idx = pd.MultiIndex.from_tuples(props,names=prop_names)

da_concat = xr.concat(da_list,'props')
da_concat.coords['props'] = prop_idx

给出:

<xarray.DataArray (props: 10,frame: 2,y: 640,x: 480)>
array([[[[0.,0.,...,0.],[0.,0.]],[[0.,0.]]],[[[1.,1.,1.],[1.,...
         [8.,8.,8.],[8.,8.]]],[[[9.,9.,9.],[9.,9.]],[[9.,9.]]]])
Coordinates:
  * frame         (frame) <U3 'Fp' 'Fmp'
  * props         (props) MultiIndex
  - experiment    (props) object 'experiment0' 'experiment1' ... 'experiment9'
  - plantbarcode  (props) int64 0 2 4 6 8 10 12 14 16 18
  - tind          (props) int64 0 1 2 3 4 5 6 7 8 9
Dimensions without coordinates: y,x
,

我在xarray邮件列表上看到了您的问题。调试这个问题很困难,因为它很复杂并且取决于您的数据。如果可以稍微简化一下,也许使用合成数据而不是数据文件,那就太好了。有关此方面的建议,请参见https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports

如果您共享print(arr)的输出,这也将有所帮助,以便我们了解您的DataArrays的内容和结构。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。