如何处理NULLS:C#,Microsoft SDS 1.3和NetCDF文件

时间:2017-12-18 02:19:58

标签: c# netcdf

我正在编写一个使用Microsoft Scientific Data-Set读取NetCDF文件的C#程序。

using System;
using System.IO;
using sds = Microsoft.Research.Science.Data;
using Microsoft.Research.Science.Data.Imperative;


namespace NetCDFConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // Gets dataset from file.
            var dataset = sds.DataSet.Open("E:\\Temp\\test.nc?openMode=readOnly");

            // Get the starting DateTime from the meta data.                        
            string dt = (string)dataset.Metadata["START_DATE"];

            //load dataset into array
            Single[,,] dataValues = dataset.GetData<float[,,]>("ACPR"); 

            //Get DateTime from Metadata fields.
            DateTime dt2 = DateTime.ParseExact(dt, "yyyy-MM-dd_HH:mm:ss", null);

            // Latitude grid ranges from = 0 to 215; East Cape is ~ 125-144
            for (int iLatitude = 137; iLatitude < 138; iLatitude++)
            {
                //Longitude ranges from 0 to 165; East Cape is ~ 125-150
                for (int iLongitude = 133; iLongitude < 134; iLongitude++) 
                {
                    //There is normally 85 hours worth of data in a file. But not always... 
                    for (int iTime = 0; iTime < 65; iTime++)
                    {
                        // Get each data point 
                        float? thisValue = dataValues[iTime,iLatitude,iLongitude]; 

                        //Burp it out to the Console. Increment the datetime while im at it. 
                        Console.WriteLine(dt.ToString() + ',' + dt2.ToString() + ',' + iTime.ToString() + ',' + dt2.AddHours(iTime) );
                    }                 
                }
            }

            Console.ReadLine();          

        }           
    }
} 

文件包含地图网格(X,Y)上的预测降雨量数据。每个网格参考应该有85小时的数据。

E:\temp>sds list test.nc
[2] ACPR of type Single (Time:85) (south_north:213) (west_east:165)
[1] Times of type SByte (Time:85) (DateStrLen:19)

但偶尔他们可能会少(比如60-70小时)。当发生这种情况时,我的C#程序在导入数据时失败。

var dataset = sds.DataSet.Open("test.nc?openMode=readOnly");
Single[,,] dataValues = dataset.GetData<Single[,,]>("ACPR");

我可以使用命令行重现错误。

在这里,我可以成功地为网格XY提取小时数60-65:125,130。我在此文件中的最后一个值是Time = 69。

E:\temp>sds data test.nc ACPR[60:65,125:125,130:130]
[2] ACPR of type Single (Time:85) (south_north:213) (west_east:165)
                Name = ACPR
         description = ACCUMULATED TOTAL GRID SCALE PRECIPITATION
         MemoryOrder = XY
         coordinates = XLONG XLAT XTIME
             stagger =
           FieldType = 104
               units = mm

[60,125,130]  13.4926
[61,125,130] 15.24556
[62,125,130]  16.3638
[63,125,130] 17.39618
[64,125,130] 20.00507
[65,125,130] 23.57192

如果我尝试阅读过去69小时,我会收到以下错误消息。

E:\temp>sds data test.nc ACPR[60:70,125:125,130:130]
[2] ACPR of type Single (Time:85) (south_north:213) (west_east:165)
                Name = ACPR
         description = ACCUMULATED TOTAL GRID SCALE PRECIPITATION
         MemoryOrder = XY
         coordinates = XLONG XLAT XTIME
             stagger =
           FieldType = 104
               units = mm

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at nc_get_vara_float(Int32 , Int32 , UInt64* , UInt64* , Single* )
   at NetCDFInterop.NetCDF.nc_get_vara_float(Int32 ncid, Int32 varid, IntPtr[] start, IntPtr[] count, Single[] data)
   at Microsoft.Research.Science.Data.NetCDF4.NetCdfVariable`1.ReadData(Int32[] origin, Int32[] shape)
   at sdsutil.Program.PrintData(Variable v, String range, String format)
   at sdsutil.Program.DoData(String uri, String[] args)
   at sdsutil.Program.Main(String[] args)

E:\temp>

如果文件包含完整的85小时,我可以请求时间0-100,它仍然给我85个值而没有错误。

我确信该问题是NULL /缺少数据。在导入变量不为空的数据时,是否有某些方法可以指定?或者使用一些try / catch?

Single[,,] dataValues = dataset.GetData<Single[,,]>("ACPR")>> where it's not blank thanks. ;

编辑:我开始怀疑文件没有正确形成。使用SDS查看器良好文件的元数据与坏看起来像这样;

Good file

Bad file

然而,命令行显示两者的元数据相同。

E:\temp>sds good.nc
[2] ACPR of type Single (Time:85) (south_north:213) (west_east:165)
[1] Times of type SByte (Time:85) (DateStrLen:19)

E:\temp>sds bad.nc
[2] ACPR of type Single (Time:85) (south_north:213) (west_east:165)
[1] Times of type SByte (Time:85) (DateStrLen:19)

E:\temp>

2 个答案:

答案 0 :(得分:2)

彼得,

由于错误在ReadData(Int32 []原点,Int32 []形状)(你指出相同);我看到两种可能的解决方案:

在深入研究解决方案之前,您需要确定是否可以将缺失的数据视为0.0,还是需要将其视为缺失。如果缺失不同于0.0,那么如果null不可接受,则可能将缺失编码为-1.0。对于缺失数据,提出-1.0值假设不可能出现负降雨值。

如果结果dataValues包含空值,那么你需要做的就是用float替换float?在行:

float thisValue = dataValues[iTime,iLatitude,iLongitude];

是:

float? thisValue = dataValues[iTime,iLatitude,iLongitude]; 

如果你在float?家中免费,那么这是一个快乐的解决方案。 (您仍然需要决定如何处理空值。)

否则可能的解决方案1)

在调用Single[,,] dataValues = dataset.GetData<Single[,,]>("ACPR");之后,确保数组的最后一个索引大小dataValues为85.潜在的GetData(..)不会填充所有85个字段,尤其是第一行数据包含少于85个字段。然后,如果需要,用0&#39; s或-1.0&#39; s手动替换空值。

然后当您检索数据时,您可以适当地处理空值,0或-1.0:

float? thisValue = dataValues[iTime,iLatitude,iLongitude];
// determine what to do with a null/0.0/-1.0 as a thisValue[..] value, 
// .. potentially continue with the next iteration

可能的解决方案2)

如果你拥有Single[,,] dataValues = dataset.GetData<Single[,,]>("ACPR");中的GetData(..)方法,那么你要确保它GetData(..)提供所有85个值和缺失值的工作是否为空/ 0&#39 ; s / -1.0&#39; s。然后,当您检索数据时,您可以适当地处理空值,0或-1.0。

干杯,

阿维

答案 1 :(得分:1)

我建议你试试这个,因为你不知道它试图返回的数据类型:

Object[,,] dataValues = dataset.GetData<object[,,]>("ACPR");

然后你可以检查循环中是否有一个有效的浮点数。

if ( dataValues[iTime,iLatitude,iLongitude] == null )
{
    float floatValue = 0;
    if (Single.TryParse(dataValues[iTime,iLatitude,iLongitude].ToString(), out floatValue)
    {
        Console.WriteLine(dt.ToString() + ',' + dt2.ToString() + ',' + iTime.ToString() + ',' + dt2.AddHours(iTime) );
    }
}