Вопрос или проблема
Я провожу анализ EOF для JJAS NDVI.
Вот данные NDVI (https://drive.google.com/drive/folders/1-QBkzTlAJRq8etuD15x_7l08woFTR86Y?usp=sharing) и коды.
from netCDF4 import Dataset
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import calendar
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.feature as cfeature
import cftime
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
%matplotlib inline
import scipy.signal
from scipy import signal
import numpy.polynomial.polynomial as poly
from eofs.standard import Eof
mpl.rcParams['figure.figsize'] = [8., 6.]
filename="D:/ERA5/ndvi331.nc"
ds = xr.open_dataset(filename)
ds
da = ds['NDVI']
da
def is_jjas(month):
return (month >= 6) & (month <= 9)
dd = da.sel(time=is_jjas(da['time.month']))
def is_1982(year):
return (year> 1981)
dn = dd.sel(time=is_1982(dd['time.year']))
dn
def lon_jjas(lon):
return (lon >= -15) & (lon <= 20)
JJ_lon = dn.sel(lon=lon_jjas(dn['lon']))
JJ2 = JJ_lon.mean(dim='lon', keep_attrs=True)
JJ2
def lat_jjas(lat):
return (lat >= 11) & (lat <= 20)
JJ_lat = JJ2.sel(lat=lat_jjas(JJ2['lat']))
JJ3 = JJ_lat.mean(dim='lat', keep_attrs=True)
JJ3 = JJ3.groupby('time.year').mean('time')
JJ3
import scipy.signal
JJ4=scipy.signal.detrend(JJ3)
JJ4
JJ4m= np.mean(JJ4, axis=0)
an4 = JJ4 - JJ4m
lons = ds['lon'].values
lats = ds['lat'].values
# Создаем решатель EOF для выполнения анализа EOF. Применяются веса
# квадратного корня косинуса широты перед вычислением EOF.
coslat = np.cos(np.deg2rad(lats)).clip(0., 1.)
wgts = np.sqrt(coslat)[..., np.newaxis]
solver = Eof(an4, weights=wgts)
eof1 = solver.eofs(neofs=10)
pc1 = solver.pcs(npcs=10, pcscaling=0)
varfrac = solver.varianceFraction()
lambdas = solver.eigenvalues()
Однако ‘solver = Eof(an4, weights=wgts)’ вызывает ошибку.
Я пытался изменить форму данных JJA4, но это не сработало.
Как я могу получить результаты eof через netCDF?
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[27], <a href="https://stackoverflow.com/questions/79006069/vscode-notebook-cell:?execution_count=27&line=1">line 1</a>
----> <a href="https://stackoverflow.com/questions/79006069/vscode-notebook-cell:?execution_count=27&line=1">1</a> solver = Eof(an4, weights=wgts)
File ~\anaconda3\Lib\site-packages\eofs\standard.py:112, in Eof.__init__(self, dataset, weights, center, ddof)
<a href="~\anaconda3\Lib\site-packages\eofs\standard.py:110">110</a> # Сохраняем входные данные в переменной экземпляра.
<a href="~\anaconda3\Lib\site-packages\eofs\standard.py:111">111</a> if dataset.ndim < 2:
--> <a href="~\anaconda3\Lib\site-packages\eofs\standard.py:112">112</a> raise ValueError('входной набор данных должен быть '
<a href="~\anaconda3\Lib\site-packages\eofs\standard.py:113">113</a> 'по крайней мере двумерным')
<a href="~\anaconda3\Lib\site-packages\eofs\standard.py:114">114</a> self._data = dataset.copy()
<a href="~\anaconda3\Lib\site-packages\eofs\standard.py:115">115</a> # Проверяем, является ли входной массив маскированным. Если да, заполните его NaN.
ValueError: входной набор данных должен быть по крайней мере двумерным
На самом деле я не понимаю, из чего должна состоять функция ‘Eof()’.
Я обнаружил, что JJ4 (дезавтраченное аномальное значение) должен иметь временное и пространственное измерение.
Добавляя и трансформируя размерные данные, ошибка все еще мешает мне!
Я хотел бы построить PC1 и PC2 (или PC1, PC2, PC3)…
https://github.com/royalosyin/Python-Practical-Application-on-Climate-Variability-Studies/blob/master/ex18-EOF%20analysis%20global%20SST.ipynb
https://ajdawson.github.io/eofs/latest/userguide/index.html
# an4 как DataArray
an4_da = xr.DataArray(an4, dims=["time", "lat", "lon"], coords={"time": ds['time'], "lat": ds['lat'], "lon": ds['lon']})
# Переносим в 2D (время x пространство)
an4_stacked = an4_da.stack(space=('lat', 'lon'))
# NaN
an4_stacked = an4_stacked.fillna(0)
Ответ или решение
Ваша задача заключается в выполнении анализа EOF (метод главных компонент) для данных NDVI (индекс состояние растительности) за период JJAS (июль, август, сентябрь, октябрь). Судя по предоставленному коду, вы уже на правильном пути, но столкнулись с ошибкой при использовании библиотеки eofs
. Давайте пройдемся по основным шагам и рассмотрим, как устранить возникшую проблему.
-
Ошибка, связанная с размерностью данных: Ошибку
ValueError: the input data set must be at least two dimensional
вы получаете, потому что переменнаяan4
имеет только одну измерение. Вам нужно создать двумерный массив, в котором строки будут представлять временные шаги, а столбцы — пространственные данные (широту и долготу). - Преобразование данных: Используйте метод
stack
, чтобы преобразовать ваш массив в нужный формат. Код, который вы уже начали писать, неплохой, но он требует некоторых корректировок.
На основе вашего кода, приведенного выше, вот подробное решение, которое поможет вам выполнить анализ EOF:
from netCDF4 import Dataset
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import xarray as xr
import scipy.signal
from eofs.standard import Eof
# Открываем данные
filename = "D:/ERA5/ndvi331.nc"
ds = xr.open_dataset(filename)
# Извлекаем данные NDVI
da = ds['NDVI']
# Фильтруем данные по сезону и времени
def is_jjas(month):
return (month >= 6) & (month <= 9)
dd = da.sel(time=is_jjas(da['time.month']))
def is_1982(year):
return (year > 1981)
dn = dd.sel(time=is_1982(dd['time.year']))
# Среднее по долготе
lon_jjas = dn.mean(dim='lon', keep_attrs=True)
# Среднее по широте
lat_jjas = lon_jjas.mean(dim='lat', keep_attrs=True)
# Дентренируем данные
an4 = scipy.signal.detrend(lat_jjas)
# Создаем DataArray с правильными размерами
an4_da = xr.DataArray(an4, dims=["time", "lat", "lon"], coords={"time": ds['time'], "lat": ds['lat'], "lon": ds['lon']})
# Переходим к двумерной форме: время x пространство
an4_stacked = an4_da.stack(space=('lat', 'lon'))
# Убедитесь, что значения NaN обрабатываются (здесь мы заполним нулями, что может быть не всегда правильно)
an4_stacked = an4_stacked.fillna(0)
# Создаем веса с учетом косинуса широты
coslat = np.cos(np.deg2rad(ds['lat'].values)).clip(0., 1.)
wgts = np.sqrt(coslat)[..., np.newaxis]
# Инициализируем класс Eof
solver = Eof(an4_stacked.values, weights=wgts)
# Получаем EOF
eof1 = solver.eofs(neofs=10)
pc1 = solver.pcs(npcs=10, pcscaling=0)
varfrac = solver.varianceFraction()
# Визуализация первых двух главных компонент
plt.figure(figsize=(12, 6))
plt.plot(pc1[:, 0], label='PC1')
plt.plot(pc1[:, 1], label='PC2')
plt.legend()
plt.title('Первые две главные компоненты (PC1 и PC2)')
plt.xlabel('Время')
plt.ylabel('Амплитуда')
plt.show()
- Пояснения по коду:
- Чтение и обработка данных: Мы фильтруем данные по сезону (JJAS) и выбираем годы после 1981 года.
- Средние значения: Мы берем средние значения по широте и долготе для упрощения анализа.
- Дентренировка: Удаляем тренды из данных, используя
scipy.signal.detrend
. - Создание DataArray: Мы создаем объект DataArray с нужными размерами и координатами.
- Анализ EOF: Мы используем библиотеку
eofs
для выполнения анализа. - Визуализация: Построение первых двух главных компонент.
Если вам нужны дополнительные пояснения или помощь в настройке, не стесняйтесь задавать вопросы. Удачи в вашем анализе!