<jupyter_start><jupyter_text>Campo multidimensionais (nD) **material de apoio**- Documentação xarray: [[link]](http://xarray.pydata.org/en/stable/index.html)- Documentação netcdf4: [[link]](https://unidata.github.io/netcdf4-python/netCDF4/index.html)-  **O que são campos multidimensionais?**Até o momento vimos diversas formas de trabalharmos com séries temporais ou informações unidimensionais. Vamos agora ver um pouco sobre campos multidimensionais, isto é, informações com mais de uma dimensão, podendo ser:- (tempo,profundidade), - (tempo,latitude,longitude),- (tempo,profundidade,latitude,longitude)- etc São tipos de informações muito utilizado por saídas de modelos numéricos (global ou regional), mas também pode observações de satélites, por exemplo. Assim, convém entendermos como e com o que analisar tais informações, bem como técnicas para realizar cálculos utilizando matrizes nD de formas simples e computacionalmente otimizadas. **Formato padrão de armazenamento**O formato mais padrão de armazenamento de arquivos com dados multidimensionais é o NetCDF, que significa: **Net**work **C**ommon **D**ata **F**ormat.O grande diferencial deste formato é a utilização combinada de dois antigos formatos de armazenamento de dados: ASCII e binário.No caso do ASCII, armazenamos as informações utilizando caracteres legíveis para nós, humanos, como no caso de um arquivo ```.dat```, por exemplo, ou em um ```.csv```. No entanto, a facilidade de lermos os dados vem com um custo e este custo é um arquivo mais pesado em termos de armazenamento. No segundo formato, binário, no entanto, armazenamos as informações na linguagem de máquina (0 e 1), reduzindo assim o tamanho do arquivo, porém não podemos interpretar facilmente o que são os dados.O formato NetCDF vem como uma solução para esta questão. Ele utiliza ambos os formatos em partes distintas do arquivo. Informações (atributos) - como metadados (dimensões, instituição que construiu o arquivo, data, etc) - são armazenados utilizando o formato ASCII. Portanto, podemos ter acesso a isso e entender o que o arquivo contem de dados. Enquanto que os dados em si são armazenados em binário, utilizando o poder de compressão de informação deste formato.<jupyter_code>!ncdump -h ../dados/nino34_ERA5.nc<jupyter_output>netcdf nino34_ERA5 {
dimensions:
	longitude = 201 ;
	latitude = 41 ;
	expver = 2 ;
	time = 500 ;
variables:
	float longitude(longitude) ;
		longitude:units = "degrees_east" ;
		longitude:long_name = "longitude" ;
	float latitude(latitude) ;
		latitude:units = "degrees_north" ;
		latitude:long_name = "latitude" ;
	int expver(expver) ;
		expver:long_name = "expver" ;
	int time(time) ;
		time:units = "hours since 1900-01-01 00:00:00.0" ;
		time:long_name = "time" ;
		time:calendar = "gregorian" ;
	short sst(time, expver, latitude, longitude) ;
		sst:scale_factor = 0.000162757933026109 ;
		sst:add_offset = 298.596354167908 ;
		sst:_FillValue = -32767s ;
		sst:missing_value = -32767s ;
		sst:units = "K" ;
		sst:long_name = "Sea surface temperature" ;

// global attributes:
		:Conventions = "CF-1.6" ;
		:history = "2020-09-30 21:02:53 GMT by grib_to_netcdf-2.16.0: /opt/ecmwf/eccodes/bin/grib_to_netcdf -S param -o /cache/data3/adaptor.mars.internal-1601499573.2579[...]<jupyter_text>**Leitura de arquivos netcdf no python**Existem, atualmente, dois pacotes para leitura de arquivos netcdf no python: netcdf e xarray.O primeiro, clássico e utilizado por muitos anos, vem sendo substituído pelo xarray, um pacote relativamente recente. Isto porquê o pacote ```netcdf4``` faz a leitura dos arquivos e cria matrizes, enquanto que o segundo, apresenta uma nova forma de analisar os dados destas informações, nos fornecendo um poderio extra em termos de análise de dados.  XarrayA estrutura de dados fornecida pelo ```numpy``` nos permite trabalhar com dados de forma bruta. No entanto, em diversos campos da ciência, o dado não se resume a apenas números. Precisamos compartilhar informações importantes, como vimos os metadados. Estas informações podem, e muitas vezes irão, influenciar a forma como analisamos os dados, seja compreendendo a região de um mapa de temperatura e sua unidade, ou acessando as informações históricas de um determinado conjunto de dados (tratamento de uma série temporal, por exemplo).![](http://xarray.pydata.org/en/stable/_images/dataset-diagram.png) No caso de campos multidimensionais **rotulados** (labeled), podemos entender que cada variável ou dimensão terá um nome e atributos associados, que explicarão o que cada informação é.Usando o exemplo da figura acima, portanto, entendemos que a variável **temperatura** contém seu nome, além de atributos que vão nos prover informações, como unidade, nome completo, alguma convenção, etc.Veremos mais a frente na prática, mas se estivéssemos utilizando matrizes do numpy, não poderíamos nomear as dimensões da matriz, podendo acessa-las apenas por números (índices).  O que é o Xarray?Trata-se de um pacote inspirado no poder do pandas, mas aplicando à matrizes nD. Ele é construído com base no Numpy, matplotlib, cartopy e pandas, além de utilizar o Dask para processamento paralelizado.Temos, com o xarray, a introdução de duas novas estruturas de dados, similares ao pandas, mas para os campos multidimensionais:- ```xr.DataArray()```Matriz multidimensional com dimensões nomeadas. Contém as dimensões, coordenadas e atributos da matriz. Basicamente é um dado com muitos metadados.- ```xr.Dataset()```Coleção de matrizes multidimensionais com dimensões alinhadas (iguais). **Algumas definições/métodos**- ```.coords``` (coordenadas)Trata-se de uma matriz, geralmente 1D, que nomeia uma coordenada específica de um ```DataArray```. Existem dois tipos de matrizes de coordendas:    - _dimension coordinates_     - _non-dimension coordinates_Podemos entendê-las como "ticklabels" e veremos o porquê mais a frente.- ```.attrs``` (atributos)É um dicionário contendo atributos arbitrários. Vamos começar a explorar o pacote ```xarray``` fazendo um pequeno comparativo em termos de processamento com o pacote ```netcdf4```.<jupyter_code># importando os dois pacotes
import netCDF4 as nc
import xarray as xr

# para ler um arquivo em cada pacote:
data_nc = nc.Dataset('/home/danilo/DATA/ERA5/SSTreanalysis/atlantic_sst2.nc')
data_xr = xr.open_dataset('/home/danilo/DATA/ERA5/SSTreanalysis/atlantic_sst2.nc')

# comparar o tipo
type(data_nc), type(data_xr)<jupyter_output><empty_output><jupyter_text>Conhecendo a cara do que cada pacote nos traz:<jupyter_code>data_nc
data_nc.variables
data_xr
lon = data_nc['longitude'][:]
lat = data_nc['latitude'][:]
sst = data_nc['sst'][0,0,:,:]

plt.figure()
plt.pcolormesh(lon, lat, sst)
data_xr.isel(time=0, expver=0)['sst'].plot.pcolormesh()<jupyter_output><empty_output><jupyter_text>Com poucas linhas, construímos uma figura de SST, já customizada com barra de cores, ticklabels (perceba as ```coords```). O pacote ```netCDF4``` ainda é utilizado por muitos pacotes python e sua utilização é relativamente simple. Aos poucos a comunidade tem migrado para utilização do ```xarray``` e daremos um enfoque a este a partir de agora.<jupyter_code># se estiver no google colab, rode:
!wget --directory-prefix=../dados/ https://raw.githubusercontent.com/nilodna/python-basico/feature_iojr-shortcourse/dados/nino34_ERA5.nc
ds = xr.open_dataset('../dados/nion34_ERA5.nc')<jupyter_output><empty_output><jupyter_text>**Acessando atributos:** ```.attrs()```Funciona tanto para dados (variáveis), quanto para coordenadas e dimensões.<jupyter_code>ds['sst'].attrs
ds.coords['longitude'].attrs
ds.time.attrs<jupyter_output><empty_output><jupyter_text>Extraindo dados por indexamentoHerda as regras de indexação do pandas, ou seja, ótimo suporte para datas e horas. **Label-based indexing**: .sel()Métodos de seleção de dados utilizando dimensões e coordenadas.<jupyter_code># selecionar registros para um ano específico
ds.sel(time='2010')
# selecionar uma coordenada específica, utilizando o método do
# mais próximo. 
ds.sel(longitude=-40, latitude=-30, method='nearest')
# Note que sem o método 'nearest', não é encontrado nenhum  valor
ds.sel(longitude=-40, latitude=-30)
# selecionando tempo com o comando slice (recorte)
ds.sel(time=slice('2010','2019'))<jupyter_output><empty_output><jupyter_text>**Position-based indexing**: .isel()Usa-se a posição (índice), em número inteiro, de uma informação. Este índice é referente à matriz 1D de coordenada ou dimensão criada na estrutura.<jupyter_code># seleção com base no
ds.isel(time=0, expver=0)['sst']
# seleção de uma coordenada pelo índice desta na coordenada referente
ds.isel(longitude=10, latitude=10)<jupyter_output><empty_output><jupyter_text>Cálculos de alto nívelTemos disponíveis métodos similares, em objetivo, aos do Pandas. Porém, precisamos nos atentar às dimensões que escolhemos para realizar os cálculos.- **Groupby:** agrupamento de informações por chaves (grupos). Altamente utilizado para computar, em conjuntos de dados longos, médias sazonais, mensais, etc.- **Resample:** aplica-se uma reamostragem seguindo uma estatística enviada.- **Rolling:** estatística móvel aplicada em uma dimensão. Note que a janela utilizada deve ser coerente com a frequência dos seus dados. Ou seja: para um dataset com frequência temporal mensal, uma janela de 3 implica em 3 meses.<jupyter_code># agrupamento temporal por ano, aplica-se a média
ds.groupby('time.year').mean()
# agrupamento temporal por estação do ano, aplica-se a média
ds.groupby('time.season').mean()
# reamostragem de mensal para anual, aplica-se a média
ds.resample(time='Y').mean()
# média móvel de 3 meses
ds.rolling(time=3).mean()<jupyter_output><empty_output><jupyter_text>Cálculo do Índice do El Niño 3.4Exemplo adaptado do tutorial fornecido pelo NCAR [[aqui]](https://ncar.github.io/PySpark4Climate/tutorials/Oceanic-Ni%C3%B1o-Index/Computational-Recipe)Receita:- média espacial na região do Niño 3.4 (ponderada pela lat/lon)- cálculo da climatologia mensal- cálculo da anomalia da média espacial- média móvel de 3 meses para suavização<jupyter_code># leitura de arquivos ERA5
ds = xr.open_dataset('../dados/nino34.nc')
ds = ds.mean(dim='expver')

# converter de Kelvin para oC
ds['sst'] -= 273.15

ds.isel(time=0).sst.plot.pcolormesh()
def global_mean_sst(dset):
    # source: https://ncar.github.io/PySpark4Climate/tutorials/Oceanic-Ni%C3%B1o-Index/#Computational-Recipe
    # Find mean temperature for each latitude
    mean_sst_lat = dset.sst.mean(dim='longitude')

    # Find Weighted mean of those values
    num =(np.cos(dset.latitude) * mean_sst_lat).sum(dim='latitude')
    denom = np.sum(np.cos(dset.latitude))

    # Find mean global temperature
    mean_global_temp = num / denom

    return mean_global_temp
# média espacial ingênua, necessário ponderar pela coordenadas,
# pois a variação de lat/lon implica em uma área diferente
ds_avg_1 = ds.mean(dim=['latitude', 'longitude'])
ds_avg_2 = global_mean_sst(ds)
# diferença entre os dois tipos de média espacial
fig,ax = plt.subplots()

ds_avg_1.sst.plot(ax=ax, label='naive')

ds_avg_2.plot(ax=ax, label='weigthed')

plt.legend()
# climatologia utilizando groupby
ds_clim = ds_avg_2.groupby('time.month').mean('time')
ds_clim
# anomalia espacial utilizando o groupby sem método estatístico
ds_anom = ds_avg_2.groupby('time.month') - ds_clim

ds_anom.plot()
# suavização com média móvel de 3 meses para calcular o 
# Oceanic Nino Index (ONI)
ds_oni = ds_anom.rolling(time=3).mean()

ds_oni.plot()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09670710571923744, 'fraction_numerical': 0.016637781629116118, 'mean_word_length': 5.429526462395543, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 36, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '610589', 'license': 'Apache-2.0', 'n_tokens_mistral': 4280, 'n_tokens_neox': 4046, 'n_words': 1272, 'path': 'material_acompanhamento/lecture07.ipynb', 'repo_name': 'nilodna/python-basico', 'size': '11540'}