Как читать файл 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 в строке])
Или что-то типа того....