如何使用Python读取NetCDF文件并将其写入CSV文件

4

我的目标是从netcdf文件中访问数据,并以以下格式将其写入CSV文件。

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

到目前为止,我已经访问了变量,将标题写入文件并填充了纬度/经度。
如何访问指定经纬度坐标和日期的MIN_SFC值,然后将其写入CSV文件。
如果有更好的方法,请告诉我,我是Python新手。
NetCDF文件信息:
Dimensions:
  time = 7 
  latitude = 292
  longitude =341

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

Here's what I've tried:

 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' object has no attribute 'append'

在Python中有任何方法可以转换为字符串吗?你认为这样会行吗?

我注意到在将数值打印到控制台时,有一些值被返回为“-”符号。我想知道这是否代表了填充值或缺失值,其定义为-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)


你为什么需要将它转换成CSV格式呢?由于数据集将数据存储为Numpy数组,所以最好使用内置的numpy.savetxt函数将其写入文本文件,具体文档可以在这里查阅 (http://docs.scipy.org/doc/numpy/reference/generated/numpy.savetxt.html)。 - Spencer Hill
你想要在经度/纬度数组中搜索给定点并找到相应的Min_SFC值吗? - DopplerShift
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Jules0080
我研究了numpy.savetext,并找到一个示例,其中插入逗号以创建CSV文件。根据我在帖子中提供的示例(表格),我不确定如何格式化输出文件中的纬度、经度、日期和sfc数据。 - Jules0080
numpy.savetxtheaderdelimiter参数。前者可以让你指定你想要的顶部行,后者可以让你根据需要插入制表符来创建所需的列。访问Numpy数组的子集(即您想要的纬度和经度范围)的最佳方法是通过Numpy的切片 - Spencer Hill
4个回答

7
我会将数据加载到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')

此示例可用于验证您所需的数据,生成以下图表: enter image description here

同时将所需的CSV文件time_series_from_netcdf.csv写入磁盘。

您还可以在Wakari上查看、下载和运行此示例


哦,我现在明白了,也许我没有仔细阅读问题。我认为需要在指定位置获取时间序列。也许这不是目标。 - Rich Signell
我正在尝试将数据写入CSV,但除了netcdf文件名和变量名称之外,我不知道任何其他信息。 - Jules0080

2

Rich Signell的答案非常有帮助!需要注意的是,还必须导入datetime,并且在提取时间时,需要使用以下代码:

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)

我遍历了所有我需要的区域,以便处理我的数据集。

0

不确定你还在遇到什么问题,这看起来很好。我注意到:

# 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日期时间对象

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

如果你想把日期转换成字符串,可以使用以下代码,但如果你只需要日期,就去掉%H:

date_strings = [dates.strftime('%Y%m%d') for date in dates]

如果你需要年、月、日的数字,datetime对象有相应的属性:

dt.year, dt.month, dt.day

至于你的sfc变量——它是一个三维数组,所以要获取特定的值,可以这样做:

sfc[time_index, lat_index, lon_index]

由于它是三维的,有多种方法可以将其写入CSV文件,但我猜你可能需要像这样的东西:

for time_index, time in enumerate(time): # 取出该时间的数据 data = sfc[time_index, :, :] # 将日期写入文件(也许) # .... 现在循环遍历“行” for row in data: outputwriter.writerow( [str(val) for val in row] )

或者类似的东西....


当我从日期字段中减去时间时,出现了属性错误,有任何想法为什么吗?我正在使用Anaconda Spyder作为我的IDE。 AttributeError: 'numpy.ndarray' object has no attribute 'strftime' date_strings = [dates.strftime('%Y%m%d') for date in dates] - Jules0080
我已经使用pip安装了numpy模块并导入了整个库,代码如下:from numpy import * - Jules0080
[type(date.strftime('%Y%m%d')) for date in dates] (您正在对“dates”序列中的每个“date”调用strftime。) - Chris Barker

0

属性错误的问题是因为content需要是一个列表,而你用lat进行初始化,它只是一个数字。你需要将其放入一个列表中。

关于3D变量,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)``

我实际上还没有尝试运行这个程序,所以可能会存在其他问题。


content = [中文]已解决错误。我发布的代码逻辑有误,对于每个lat索引,代码都会迭代所有lon索引。在调用data = sfc[time_index,lat_index,lon_index]时,应确保使用相同的索引来处理lat和lon。这是目前的主要问题,下一个代码块迭代时间是正确的,因为我需要相同lat/lon的每个时间的sfc值。 - Jules0080
DopplerShift 已经生效。我将 outputwriter 移到了时间循环之外,现在行已经正确地写入文件中了。 - Jules0080
zip(lats, lons) 不能给出每个纬度/经度的组合。应该使用 itertools.product 中的 product(lats, lons) - alphabetasoup

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接