domino.filtering

  1import numpy as np
  2from scipy.ndimage import label
  3import itertools as it
  4import xarray as xr
  5
  6def latlonarea(lat0,lat1,lon0,lon1,r=6731):
  7    theta=np.deg2rad((lat1+lat0)/2)
  8    r2=r**2
  9    dphi=lon1-lon0
 10    dtheta=lat1-lat0
 11    A=r2*np.abs(np.cos(theta))*dtheta*dphi #in km**2
 12    return A
 13
 14def grid_area(da,lat_coord='lat',lon_coord='lon',r=6371):
 15    lat=da[lat_coord].values
 16    lon=da[lon_coord].values
 17    dlon=(lon[1:]-lon[:-1])/2
 18    dlat=(lat[1:]-lat[:-1])/2
 19    dlon=[np.median(dlon),*dlon]
 20    dlat=[np.median(dlat),*dlat]
 21    areas=np.array([[\
 22        latlonarea(la-dla,la+dla,lo-dlo,lo+dlo,r=r)\
 23        for la,dla in zip(lat,dlat)]\
 24        for lo,dlo in zip(lon,dlon)])
 25    area=xr.DataArray(data=areas.T,\
 26        coords={lat_coord:lat,lon_coord:lon})
 27    return area
 28
 29
 30def apply_2d_func_to_da(da,func,*args,dims=None):
 31    da=da.copy(deep=True)
 32    da_dim=None    
 33    if dims is not None:
 34        #if dims is specified, ignore da's without all dims
 35        if not set(dims).issubset(set(da.dims)):
 36            return da
 37        #Put dims at the front
 38        else:
 39            da_dim=list(da.dims)
 40            da=da.transpose(*dims,...)
 41            
 42        #Since funcs are 2D,
 43        #we must add a dummy dim if we only want 1 dim
 44        if len(dims)==1:
 45            da=da.expand_dims('dummy_dim',0).copy(deep=True)
 46            
 47    #We now want to loop over all dims except the first two
 48    #and apply our function
 49    indexers=[np.arange(i) for i in da.shape[2:]]
 50    for i in it.product(*indexers):
 51        da[(...,*i)]=func(da[(...,*i)].values,*args)
 52    try:
 53        da=da.isel(dummy_dim=0)
 54    except Exception as e:
 55        pass
 56    if da_dim is not None:
 57        da=da.transpose(*da_dim)    
 58    return da
 59
 60def get_large_regions(grid,point_thresh):
 61    connected,ncon=label(grid)
 62    connected=connected.astype(float)
 63    ix,n=np.unique(connected,return_counts=True)
 64    for m in ix[n<point_thresh]:
 65        connected[connected==m]=0
 66    return connected>0
 67
 68def get_area_regions(grid,area_thresh,area):
 69    connected,ncon=label(grid)
 70    connected=connected.astype(float)
 71    ix,n=np.unique(connected,return_counts=True)
 72    
 73    connected_da=xr.DataArray(connected,coords=area.coords)
 74    ix_areas=np.array([area.where(connected_da==i).sum().values\
 75              for i in ix])
 76    for m in ix[ix_areas<area_thresh]:
 77        connected[connected==m]=0
 78    return connected>0
 79
 80
 81def da_large_regions(da,n,dims,area_based=False):
 82    
 83    if area_based:
 84        
 85        if len(dims)!=2:
 86            raise(ValueError('Area weighted n currently only supported for two dimensions, assumed to be (lat,lon)'))
 87        lat,lon=dims
 88        #area in units of solid angle
 89        area=grid_area(da,lat_coord=lat,lon_coord=lon,r=1)
 90        
 91        return apply_2d_func_to_da(da,get_area_regions,n,area,dims=dims)
 92    else:
 93        return apply_2d_func_to_da(da,get_large_regions,n,dims=dims)
 94
 95def ds_large_regions(mask_ds,n,dims,area_based=False):
 96        ds=mask_ds.copy()
 97        for var in list(ds.data_vars):
 98            
 99            if (dims is not None) and (not np.all([d in mask_ds[var].dims for d in dims])):
100                pass #ignore variables with missing dims
101            else:
102                ds[var]=da_large_regions(ds[var],n,dims,area_based=area_based)
103        return ds
104
105def convolve_pad(x,N):
106    Y=np.array([np.convolve(y,np.ones(np.min([N,len(y)])),mode='same') for y in x])
107    Y=np.array([np.convolve(y,np.ones(np.min([N,len(y)])),mode='same') for y in Y.T]).T
108    Y[Y>0]=1
109    return Y
110
111def convolve_pad_da(da,n,dims):
112    return apply_2d_func_to_da(da,convolve_pad,n,dims=dims)
113
114def convolve_pad_ds(ds,n,dims):
115    ds=ds.copy()
116    for var in list(ds.data_vars):
117        ds[var]=convolve_pad_da(ds[var],n,dims=dims)
118    return ds
119
120def std_filter(val_ds,std,frac):
121    return np.abs(val_ds)>=frac*std
def latlonarea(lat0, lat1, lon0, lon1, r=6731):
 7def latlonarea(lat0,lat1,lon0,lon1,r=6731):
 8    theta=np.deg2rad((lat1+lat0)/2)
 9    r2=r**2
10    dphi=lon1-lon0
11    dtheta=lat1-lat0
12    A=r2*np.abs(np.cos(theta))*dtheta*dphi #in km**2
13    return A
def grid_area(da, lat_coord='lat', lon_coord='lon', r=6371):
15def grid_area(da,lat_coord='lat',lon_coord='lon',r=6371):
16    lat=da[lat_coord].values
17    lon=da[lon_coord].values
18    dlon=(lon[1:]-lon[:-1])/2
19    dlat=(lat[1:]-lat[:-1])/2
20    dlon=[np.median(dlon),*dlon]
21    dlat=[np.median(dlat),*dlat]
22    areas=np.array([[\
23        latlonarea(la-dla,la+dla,lo-dlo,lo+dlo,r=r)\
24        for la,dla in zip(lat,dlat)]\
25        for lo,dlo in zip(lon,dlon)])
26    area=xr.DataArray(data=areas.T,\
27        coords={lat_coord:lat,lon_coord:lon})
28    return area
def apply_2d_func_to_da(da, func, *args, dims=None):
31def apply_2d_func_to_da(da,func,*args,dims=None):
32    da=da.copy(deep=True)
33    da_dim=None    
34    if dims is not None:
35        #if dims is specified, ignore da's without all dims
36        if not set(dims).issubset(set(da.dims)):
37            return da
38        #Put dims at the front
39        else:
40            da_dim=list(da.dims)
41            da=da.transpose(*dims,...)
42            
43        #Since funcs are 2D,
44        #we must add a dummy dim if we only want 1 dim
45        if len(dims)==1:
46            da=da.expand_dims('dummy_dim',0).copy(deep=True)
47            
48    #We now want to loop over all dims except the first two
49    #and apply our function
50    indexers=[np.arange(i) for i in da.shape[2:]]
51    for i in it.product(*indexers):
52        da[(...,*i)]=func(da[(...,*i)].values,*args)
53    try:
54        da=da.isel(dummy_dim=0)
55    except Exception as e:
56        pass
57    if da_dim is not None:
58        da=da.transpose(*da_dim)    
59    return da
def get_large_regions(grid, point_thresh):
61def get_large_regions(grid,point_thresh):
62    connected,ncon=label(grid)
63    connected=connected.astype(float)
64    ix,n=np.unique(connected,return_counts=True)
65    for m in ix[n<point_thresh]:
66        connected[connected==m]=0
67    return connected>0
def get_area_regions(grid, area_thresh, area):
69def get_area_regions(grid,area_thresh,area):
70    connected,ncon=label(grid)
71    connected=connected.astype(float)
72    ix,n=np.unique(connected,return_counts=True)
73    
74    connected_da=xr.DataArray(connected,coords=area.coords)
75    ix_areas=np.array([area.where(connected_da==i).sum().values\
76              for i in ix])
77    for m in ix[ix_areas<area_thresh]:
78        connected[connected==m]=0
79    return connected>0
def da_large_regions(da, n, dims, area_based=False):
82def da_large_regions(da,n,dims,area_based=False):
83    
84    if area_based:
85        
86        if len(dims)!=2:
87            raise(ValueError('Area weighted n currently only supported for two dimensions, assumed to be (lat,lon)'))
88        lat,lon=dims
89        #area in units of solid angle
90        area=grid_area(da,lat_coord=lat,lon_coord=lon,r=1)
91        
92        return apply_2d_func_to_da(da,get_area_regions,n,area,dims=dims)
93    else:
94        return apply_2d_func_to_da(da,get_large_regions,n,dims=dims)
def ds_large_regions(mask_ds, n, dims, area_based=False):
 96def ds_large_regions(mask_ds,n,dims,area_based=False):
 97        ds=mask_ds.copy()
 98        for var in list(ds.data_vars):
 99            
100            if (dims is not None) and (not np.all([d in mask_ds[var].dims for d in dims])):
101                pass #ignore variables with missing dims
102            else:
103                ds[var]=da_large_regions(ds[var],n,dims,area_based=area_based)
104        return ds
def convolve_pad(x, N):
106def convolve_pad(x,N):
107    Y=np.array([np.convolve(y,np.ones(np.min([N,len(y)])),mode='same') for y in x])
108    Y=np.array([np.convolve(y,np.ones(np.min([N,len(y)])),mode='same') for y in Y.T]).T
109    Y[Y>0]=1
110    return Y
def convolve_pad_da(da, n, dims):
112def convolve_pad_da(da,n,dims):
113    return apply_2d_func_to_da(da,convolve_pad,n,dims=dims)
def convolve_pad_ds(ds, n, dims):
115def convolve_pad_ds(ds,n,dims):
116    ds=ds.copy()
117    for var in list(ds.data_vars):
118        ds[var]=convolve_pad_da(ds[var],n,dims=dims)
119    return ds
def std_filter(val_ds, std, frac):
121def std_filter(val_ds,std,frac):
122    return np.abs(val_ds)>=frac*std