Как работать с 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) );
}
}