Как работать с NULL: C#, Microsoft SDS 1.3 и NetCDF файлы

Я пишу программу на C#, которая использует Microsoft Scientific Data-Set для чтения файлов 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");

Я могу воспроизвести ошибку с помощью командной строки.

Здесь я могу успешно извлечь часы 60-65 для сетки XY: 125 130. Последнее значение, которое у меня есть в этом файле: время =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 значений без ошибок.

Я убежден, что эта проблема пустая или отсутствует. Есть ли способ указать при импорте данных, где переменная не равна нулю? или использовать что-то вроде try/catch?

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

Изменить: я начинаю подозревать, что файл не был сформирован правильно. Использование средства просмотра SDS Метаданные для хорошего файла и плохой внешний вид;

Хороший файл

Плохой файл

Тем не менее, командная строка показывает метаданные как одинаковые для обоих.

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 ответа

Питер,

Так как ошибка в ReadData(происхождение Int32[], форма Int32[]) (Вы указали то же самое); Я вижу два возможных решения:

Прежде чем углубляться в решение, вам необходимо решить, можно ли считать отсутствующие данные 0.0, или же их следует рассматривать как отсутствующие. Если пропущенное значение отличается от 0,0, то потенциально пропущенное может быть закодировано как -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 или -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)

Если у вас есть метод GetData(..) в Single[,,] dataValues = dataset.GetData<Single[,,]>("ACPR"); тогда вы убедитесь, что это, GetData(..), обеспечивает работу по предоставлению всех 85 значений и пропущенных значений в виде нулей / 0 / -1.0. Затем, когда вы получаете данные, вы обрабатываете нули, 0 или -1,0 соответственно.

Ура,

Avi

Я рекомендую вам попробовать это, так как вы не знаете тип данных, которые он пытается вернуть:

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) );
    }
}
Другие вопросы по тегам