2017-12-18 73 views
5

我在写一个使用Microsoft Scientific Data Set读取NetCDF文件的C#程序。如何处理NULLS:C#,Microsoft SDS 1.3和NetCDF文件

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:125,130小时60-65。我在这个文件中的最后一个值是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> 
+0

我不知道我是否可以使用通用C#函数来处理NULLS,或者如果我需要使用SDS中的某些内容。 – Peter

+0

来自doc:“当您使用C#等强类型语言调用Scientific DataSet方法时,科学数据集库不会强制数据类型。数据集中的数据类型和您指定的数据类型为 类型参数到GetData方法必须完全匹配。“。 'Single [,,] dataValues = dataset.GetData (“ACPR”); '这是单身,浮动,双等?什么'var dataValues = dataset.GetData (“ACPR”);'当你尝试不同类型?你是否已经安装了数据集查看器,如果可以的话,你可以添加'dataset.View();' –

+0

请注意我的“从文档”评论对这里使用的库做了一个假设,这个链接可能是有用的。 –

回答

2

彼得,

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

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

如果结果dataValues包含空值,您可能需要做的所有操作都是用浮点数替换浮点数?在该行:

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或-1.0手动替换空值。

然后,当您检索数据,你处理空值,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(..)方法,那么你确保它提供全部85个值和缺失值的工作是否为零/ 0/-1.0。然后,当您检索数据时,您会适当地处理零值,0或-1.0。

干杯,

阿维

+0

谢谢Avi。我想我明白了。我今天会处理它。我不太清楚你的意思是“如果你拥有GetData(..)”这是我使用的SDS库中的一个函数,我没有自己写,我只是利用它。 – Peter

+0

你是对的:降雨不能是负面的。不过,我喜欢你的建议,很乐意使用-1并稍后处理。但我仍然不知道如何做到这一点。我已经更新了我的代码示例,为简单起见,我已将其缩减,但不幸的是它有错别字。 – Peter

+0

我试过你的建议:浮动?但在此之前发生错误。 (第20行不是第35行)。它失败:Single [,,] dataValues = dataset.GetData (“ACPR”);我无法弄清楚如何使用“?”在这方面。如果有帮助,我可以将Single更改为float。 – Peter

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)); 
    } 
} 
+0

感谢您的回复。我只是试过,但VS似乎并不喜欢它。我用TryParse得到2个警告/错误:1.“不能从对象转换为字符串”,以及2.“必须使用out关键字传递参数”。抱歉,我是C#的绝对新手。 – Peter

+0

对代码进行了更改 – Ctznkane525

+0

Object [,,] dataValues = dataset.GetData (“ACPR”); 但它现在抛出一个不同的错误:消息=请求的变量不存在于数据集 – Peter