Как читать файл NetCDF и писать в CSV, используя Python

Моя цель - получить доступ к данным из файла netcdf и записать их в файл CSV в следующем формате.

Latitude  Longitude Date1  Date2  Date3
100       200       <-- MIN_SFC values -->

До сих пор я обращался к переменным, записывал заголовок в файл и заполнял латы.

Как получить доступ к значениям MIN_SFC для указанных координат lon,lat и дат, а затем записать их в файл CSV.

Я новичок в питоне, если есть лучший способ сделать это, пожалуйста, дайте мне знать.

Информация о файле NetCDF:

Dimensions:
  time = 7 
  latitude = 292
  longitude =341

Variables:
  float MIN_SFC (time=7, latitude = 292, longitude = 341)

Вот что я попробовал:

 from netCDF4 import Dataset, num2date

 filename = "C:/filename.nc"

 nc = Dataset(filename, 'r', Format='NETCDF4')
 print nc.variables

 print 'Variable List'

 for var in nc.variables:
    print var, var.units, var.shape

 # get coordinates variables
 lats = nc.variables['latitude'][:]
 lons = nc.variables['longitude'][:]

 sfc= nc.variables['Min_SFC'][:]
 times = nc.variables['time'][:]

 # convert date, how to store date only strip away time?
 print "Converting Dates"
 units = nc.variables['time'].units
 dates = num2date (times[:], units=units, calendar='365_day')

 #print [dates.strftime('%Y%m%d%H') for date in dates]

 header = ['Latitude', 'Longitude']

 # append dates to header string

 for d in dates:
    print d
    header.append(d)

 # write to file
 import csv

 with open('Output.csv', 'wb') as csvFile:
    outputwriter = csv.writer(csvFile, delimiter=',')
    outputwriter.writerow(header)
    for lat, lon in zip(lats, lons):
      outputwriter.writerow( [lat, lon] )
 
 # close the output file
 csvFile.close()

 # close netcdf
 nc.close()

ОБНОВИТЬ:

Я обновил код, который записывает файл CSV, есть ошибка атрибута, потому что широта / долгота двойные.

AttributeError: у объекта 'numpy.float32' нет атрибута 'append'

Любой способ привести к строке в Python? Как вы думаете, это будет работать?

Я заметил несколько значений, возвращаемых как "-", когда я печатал значения в консоль. Мне интересно, представляет ли это fillValue или missingValue, определенный как -32767.0.

Мне также интересно, должны ли переменные набора данных 3d получать доступ с помощью lats = nc.variables['latitude'][:][:] или lats = nc.variables['latitude'][:][:,:]?

# the csv file is closed when you leave the block
with open('output.csv', 'wb') as csvFile:
    outputwriter = csv.writer(csvFile, delimiter=',')
    for time_index, time in enumerate(times): # pull the dates out for the header
         t = num2date(time, units = units, calendar='365_day')
         header.append(t)
    outputwriter.writerow(header)  
    for lat_index, lat in enumerate(lats):
        content = lat
        print lat_index
        for lon_index, lon in enumerate(lons):
            content.append(lon)
            print lon_index    
            for time_index, time in enumerate(times): # for a date
                # pull out the data 
                data = sfc[time_index,lat_index,lon_index]
                content.append(data)
                outputwriter.writerow(content)

4 ответа

Я бы загружал данные в Pandas, что облегчает анализ и построение данных временных рядов, а также запись в CSV.

Итак, вот реальный рабочий пример, который извлекает временные ряды высот волн из заданного местоположения lon, lat из набора данных модели глобального прогноза.

Примечание: здесь мы получаем доступ к набору данных OPeNDAP, поэтому мы можем просто извлечь нужные данные с удаленного сервера без загрузки файлов. Но netCDF4 работает точно так же для удаления набора данных OPeNDAP или локального файла NetCDF, что является очень полезной функцией!

import netCDF4
import pandas as pd
import matplotlib.pyplot as plt

# NetCDF4-Python can read a remote OPeNDAP dataset or a local NetCDF file:
url='http://thredds.ucar.edu/thredds/dodsC/grib/NCEP/WW3/Global/Best'
nc = netCDF4.Dataset(url)
nc.variables.keys()

lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
time_var = nc.variables['time']
dtime = netCDF4.num2date(time_var[:],time_var.units)

# determine what longitude convention is being used [-180,180], [0,360]
print lon.min(),lon.max()

# specify some location to extract time series
lati = 41.4; loni = -67.8 +360.0  # Georges Bank

# find closest index to specified value
def near(array,value):
    idx=(abs(array-value)).argmin()
    return idx

# Find nearest point to desired location (could also interpolate, but more work)
ix = near(lon, loni)
iy = near(lat, lati)

# Extract desired times.      
# 1. Select -+some days around the current time:
start = dt.datetime.utcnow()- dt.timedelta(days=3)
stop = dt.datetime.utcnow()+ dt.timedelta(days=3)
#       OR
# 2. Specify the exact time period you want:
#start = dt.datetime(2013,6,2,0,0,0)
#stop = dt.datetime(2013,6,3,0,0,0)

istart = netCDF4.date2index(start,time_var,select='nearest')
istop = netCDF4.date2index(stop,time_var,select='nearest')
print istart,istop

# Get all time records of variable [vname] at indices [iy,ix]
vname = 'Significant_height_of_wind_waves_surface'
#vname = 'surf_el'
var = nc.variables[vname]
hs = var[istart:istop,iy,ix]
tim = dtime[istart:istop]

# Create Pandas time series object
ts = pd.Series(hs,index=tim,name=vname)

# Use Pandas time series plot method
ts.plot(figsize(12,4),
   title='Location: Lon=%.2f, Lat=%.2f' % ( lon[ix], lat[iy]),legend=True)
plt.ylabel(var.units);

#write to a CSV file
ts.to_csv('time_series_from_netcdf.csv')

который создает этот график, чтобы убедиться, что вы получили нужные данные:

а также записывает нужный файл CSV time_series_from_netcdf.csv на диск.

Вы также можете просмотреть, скачать и / или запустить этот пример на Wakari.

Ответ Рича Синьелла был невероятно полезным! Как примечание, важно также импортировать дату и время, а при извлечении времени необходимо использовать следующий код:

import datetime
import netCDF4
import pandas as pd
import matplotlib.pyplot as plt

...

# 2. Specify the exact time period you want:
start = datetime.datetime(2005,1,1,0,0,0)
stop = datetime.datetime(2010,12,31,0,0,0)

Затем я перебрал все регионы, которые мне нужны для моего набора данных.

Проблема с ошибкой атрибута заключается в том, что content должен быть список, и вы инициализируете его lat, который является просто числом. Вы должны поместить это в список.

Что касается трехмерных переменных, lats = nc.variables['latitude'][:] достаточно, чтобы прочитать все данные.

Обновление: итерация по lon / lat вместе

Вот ваш код с модом для списка и итерации:

# the csv file is closed when you leave the block
with open('output.csv', 'wb') as csvFile:
    outputwriter = csv.writer(csvFile, delimiter=',')
    for time_index, time in enumerate(times): # pull the dates out for the header
        t = num2date(time, units = units, calendar='365_day')
        header.append(t)
    outputwriter.writerow(header)

    for latlon_index, (lat,lon) in enumerate(zip(lats, lons)):
        content = [lat, lon] # Put lat and lon into list
        print latlon_index
        for time_index, time in enumerate(times): # for a date
            # pull out the data 
            data = sfc[time_index,lat_index,lon_index]
            content.append(data)
            outputwriter.writerow(content)``

Я на самом деле не пытался запустить это, поэтому могут быть другие проблемы, скрывающиеся.

Не уверен, что у вас все еще проблемы, это выглядит хорошо. Я вижу:

# convert date, how to store date only strip away time?
 print "Converting Dates"
 units = nc.variables['time'].units
 dates = num2date (times[:], units=units, calendar='365_day')

теперь у вас есть даты как объекты Python Datetime

 #print [dates.strftime('%Y%m%d%H') for date in dates]

и это то, что вам нужно, если вы хотите, чтобы они были в виде строк, но если вы хотите только день, удалите% H:

date_strings = [date.strftime('%Y%m%d') для даты в датах]

если вы хотите, чтобы год, месяц день были числами, объекты datetime имеют атрибуты для этого:

день года, день месяца, день рождения

Что касается вашей переменной sfc - это 3-й массив, поэтому для получения определенного значения вы можете сделать:

SFC [time_index, lat_index, lon_index]

в 3-й степени существует более одного способа записать его в CSV-файл, но я предполагаю, что вам может понадобиться что-то вроде:

для time_index время в enumerate(time): # извлекает данные за это время data = sfc[time_index,:,:] # записывает дату в файл (возможно) # .... Теперь перебираем "строки" для строки в данных: outputwriter.writerow( [str(val) для val в строке])

Или что-то типа того....

Другие вопросы по тегам