trufl 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
trufl/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.0.2"
trufl/_modidx.py ADDED
@@ -0,0 +1,81 @@
1
+ # Autogenerated by nbdev
2
+
3
+ d = { 'settings': { 'branch': 'main',
4
+ 'doc_baseurl': '/trufl',
5
+ 'doc_host': 'https://franckalbinet.github.io',
6
+ 'git_url': 'https://github.com/franckalbinet/trufl',
7
+ 'lib_path': 'trufl'},
8
+ 'syms': { 'trufl.callbacks': { 'trufl.callbacks.Callback': ('callbacks.html#callback', 'trufl/callbacks.py'),
9
+ 'trufl.callbacks.CountCB': ('callbacks.html#countcb', 'trufl/callbacks.py'),
10
+ 'trufl.callbacks.CountCB.__call__': ('callbacks.html#countcb.__call__', 'trufl/callbacks.py'),
11
+ 'trufl.callbacks.CountCB.__init__': ('callbacks.html#countcb.__init__', 'trufl/callbacks.py'),
12
+ 'trufl.callbacks.MaxCB': ('callbacks.html#maxcb', 'trufl/callbacks.py'),
13
+ 'trufl.callbacks.MaxCB.__call__': ('callbacks.html#maxcb.__call__', 'trufl/callbacks.py'),
14
+ 'trufl.callbacks.MaxCB.__init__': ('callbacks.html#maxcb.__init__', 'trufl/callbacks.py'),
15
+ 'trufl.callbacks.MinCB': ('callbacks.html#mincb', 'trufl/callbacks.py'),
16
+ 'trufl.callbacks.MinCB.__call__': ('callbacks.html#mincb.__call__', 'trufl/callbacks.py'),
17
+ 'trufl.callbacks.MinCB.__init__': ('callbacks.html#mincb.__init__', 'trufl/callbacks.py'),
18
+ 'trufl.callbacks.MoranICB': ('callbacks.html#moranicb', 'trufl/callbacks.py'),
19
+ 'trufl.callbacks.MoranICB.__call__': ('callbacks.html#moranicb.__call__', 'trufl/callbacks.py'),
20
+ 'trufl.callbacks.MoranICB.__init__': ('callbacks.html#moranicb.__init__', 'trufl/callbacks.py'),
21
+ 'trufl.callbacks.MoranICB._weights': ('callbacks.html#moranicb._weights', 'trufl/callbacks.py'),
22
+ 'trufl.callbacks.PriorCB': ('callbacks.html#priorcb', 'trufl/callbacks.py'),
23
+ 'trufl.callbacks.PriorCB.__call__': ('callbacks.html#priorcb.__call__', 'trufl/callbacks.py'),
24
+ 'trufl.callbacks.PriorCB.__init__': ('callbacks.html#priorcb.__init__', 'trufl/callbacks.py'),
25
+ 'trufl.callbacks.State': ('callbacks.html#state', 'trufl/callbacks.py'),
26
+ 'trufl.callbacks.State.__call__': ('callbacks.html#state.__call__', 'trufl/callbacks.py'),
27
+ 'trufl.callbacks.State.__init__': ('callbacks.html#state.__init__', 'trufl/callbacks.py'),
28
+ 'trufl.callbacks.State._flatten': ('callbacks.html#state._flatten', 'trufl/callbacks.py'),
29
+ 'trufl.callbacks.State.expand_to_k_nearest': ( 'callbacks.html#state.expand_to_k_nearest',
30
+ 'trufl/callbacks.py'),
31
+ 'trufl.callbacks.State.get': ('callbacks.html#state.get', 'trufl/callbacks.py'),
32
+ 'trufl.callbacks.State.run_cbs': ('callbacks.html#state.run_cbs', 'trufl/callbacks.py'),
33
+ 'trufl.callbacks.StdCB': ('callbacks.html#stdcb', 'trufl/callbacks.py'),
34
+ 'trufl.callbacks.StdCB.__call__': ('callbacks.html#stdcb.__call__', 'trufl/callbacks.py'),
35
+ 'trufl.callbacks.StdCB.__init__': ('callbacks.html#stdcb.__init__', 'trufl/callbacks.py'),
36
+ 'trufl.callbacks.Variable': ('callbacks.html#variable', 'trufl/callbacks.py')},
37
+ 'trufl.collector': { 'trufl.collector.DataCollector': ('collector.html#datacollector', 'trufl/collector.py'),
38
+ 'trufl.collector.DataCollector.__init__': ('collector.html#datacollector.__init__', 'trufl/collector.py'),
39
+ 'trufl.collector.DataCollector.collect': ('collector.html#datacollector.collect', 'trufl/collector.py'),
40
+ 'trufl.collector.DataCollector.get_values': ( 'collector.html#datacollector.get_values',
41
+ 'trufl/collector.py')},
42
+ 'trufl.mcdm': { 'trufl.mcdm.abspearson': ('mcdm.html#abspearson', 'trufl/mcdm.py'),
43
+ 'trufl.mcdm.check_normalization_input': ('mcdm.html#check_normalization_input', 'trufl/mcdm.py'),
44
+ 'trufl.mcdm.check_scoring_input': ('mcdm.html#check_scoring_input', 'trufl/mcdm.py'),
45
+ 'trufl.mcdm.check_weighting_input': ('mcdm.html#check_weighting_input', 'trufl/mcdm.py'),
46
+ 'trufl.mcdm.correlate': ('mcdm.html#correlate', 'trufl/mcdm.py'),
47
+ 'trufl.mcdm.cp': ('mcdm.html#cp', 'trufl/mcdm.py'),
48
+ 'trufl.mcdm.critic': ('mcdm.html#critic', 'trufl/mcdm.py'),
49
+ 'trufl.mcdm.dcor': ('mcdm.html#dcor', 'trufl/mcdm.py'),
50
+ 'trufl.mcdm.dist_matrix': ('mcdm.html#dist_matrix', 'trufl/mcdm.py'),
51
+ 'trufl.mcdm.em': ('mcdm.html#em', 'trufl/mcdm.py'),
52
+ 'trufl.mcdm.is_normalized_matrix': ('mcdm.html#is_normalized_matrix', 'trufl/mcdm.py'),
53
+ 'trufl.mcdm.is_normalized_vector': ('mcdm.html#is_normalized_vector', 'trufl/mcdm.py'),
54
+ 'trufl.mcdm.lin_func': ('mcdm.html#lin_func', 'trufl/mcdm.py'),
55
+ 'trufl.mcdm.linear1': ('mcdm.html#linear1', 'trufl/mcdm.py'),
56
+ 'trufl.mcdm.linear2': ('mcdm.html#linear2', 'trufl/mcdm.py'),
57
+ 'trufl.mcdm.linear3': ('mcdm.html#linear3', 'trufl/mcdm.py'),
58
+ 'trufl.mcdm.mw': ('mcdm.html#mw', 'trufl/mcdm.py'),
59
+ 'trufl.mcdm.normalize': ('mcdm.html#normalize', 'trufl/mcdm.py'),
60
+ 'trufl.mcdm.pearson': ('mcdm.html#pearson', 'trufl/mcdm.py'),
61
+ 'trufl.mcdm.score': ('mcdm.html#score', 'trufl/mcdm.py'),
62
+ 'trufl.mcdm.sd': ('mcdm.html#sd', 'trufl/mcdm.py'),
63
+ 'trufl.mcdm.squared_dcor': ('mcdm.html#squared_dcor', 'trufl/mcdm.py'),
64
+ 'trufl.mcdm.squared_dcov': ('mcdm.html#squared_dcov', 'trufl/mcdm.py'),
65
+ 'trufl.mcdm.squared_dcov_matrix': ('mcdm.html#squared_dcov_matrix', 'trufl/mcdm.py'),
66
+ 'trufl.mcdm.topsis': ('mcdm.html#topsis', 'trufl/mcdm.py'),
67
+ 'trufl.mcdm.vector': ('mcdm.html#vector', 'trufl/mcdm.py'),
68
+ 'trufl.mcdm.vic': ('mcdm.html#vic', 'trufl/mcdm.py'),
69
+ 'trufl.mcdm.weigh': ('mcdm.html#weigh', 'trufl/mcdm.py')},
70
+ 'trufl.optimizer': { 'trufl.optimizer.Optimizer': ('optimizer.html#optimizer', 'trufl/optimizer.py'),
71
+ 'trufl.optimizer.Optimizer.__init__': ('optimizer.html#optimizer.__init__', 'trufl/optimizer.py'),
72
+ 'trufl.optimizer.Optimizer.rank': ('optimizer.html#optimizer.rank', 'trufl/optimizer.py')},
73
+ 'trufl.reader': { 'trufl.reader.read_geojson': ('reader.html#read_geojson', 'trufl/reader.py'),
74
+ 'trufl.reader.read_shapefile': ('reader.html#read_shapefile', 'trufl/reader.py')},
75
+ 'trufl.sampler': { 'trufl.sampler.Sampler': ('sampler.html#sampler', 'trufl/sampler.py'),
76
+ 'trufl.sampler.Sampler.__init__': ('sampler.html#sampler.__init__', 'trufl/sampler.py'),
77
+ 'trufl.sampler.Sampler.loc_ids': ('sampler.html#sampler.loc_ids', 'trufl/sampler.py'),
78
+ 'trufl.sampler.Sampler.sample': ('sampler.html#sampler.sample', 'trufl/sampler.py')},
79
+ 'trufl.utils': { 'trufl.utils.anonymize_raster': ('utils.html#anonymize_raster', 'trufl/utils.py'),
80
+ 'trufl.utils.gridder': ('utils.html#gridder', 'trufl/utils.py'),
81
+ 'trufl.utils.reproject_raster': ('utils.html#reproject_raster', 'trufl/utils.py')}}}
trufl/callbacks.py ADDED
@@ -0,0 +1,184 @@
1
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/04_callbacks.ipynb.
2
+
3
+ # %% auto 0
4
+ __all__ = ['Variable', 'Callback', 'State', 'MaxCB', 'MinCB', 'StdCB', 'CountCB', 'MoranICB', 'PriorCB']
5
+
6
+ # %% ../nbs/04_callbacks.ipynb 2
7
+ from dataclasses import dataclass
8
+
9
+ import warnings
10
+ warnings.filterwarnings("ignore", category=UserWarning, message="The weights matrix is not fully connected")
11
+
12
+ # %% ../nbs/04_callbacks.ipynb 3
13
+ from pysal.lib import weights
14
+ from pysal.explore import esda
15
+ import itertools
16
+ import fastcore.all as fc
17
+ from fastcore.basics import patch
18
+ import numpy as np
19
+ from scipy.spatial import KDTree
20
+ import geopandas as gpd
21
+ from typing import List
22
+ from collections.abc import Callable
23
+ import rasterio
24
+ from rasterio.mask import mask
25
+ import pandas as pd
26
+ from typing import Type
27
+
28
+ # %% ../nbs/04_callbacks.ipynb 5
29
+ @dataclass
30
+ class Variable:
31
+ "State variable"
32
+ name: str
33
+ value: float
34
+
35
+ # %% ../nbs/04_callbacks.ipynb 6
36
+ class Callback(): pass
37
+
38
+ # %% ../nbs/04_callbacks.ipynb 8
39
+ class State:
40
+ def __init__(self,
41
+ measurements:gpd.GeoDataFrame, # Measurements data with `loc_id`, `geometry` and `value` columns.
42
+ smp_areas:gpd.GeoDataFrame, # Grid of areas/polygons of interest with `loc_id` and `geometry`.
43
+ cbs:List[Callable], # List of Callback functions returning `Variable`s.
44
+ ):
45
+ "Collect various variables/metrics per grid cell/administrative unit."
46
+ fc.store_attr()
47
+ self.unsampled_locs = self.smp_areas.index.difference(self.measurements.index)
48
+
49
+ # %% ../nbs/04_callbacks.ipynb 9
50
+ @patch
51
+ def get(self:State,
52
+ loc_id:str, # Unique id of the Point feature
53
+ as_numpy=False # Whether or not to return a list of `Variable` or a tuple of numpy arrays.
54
+ ):
55
+ "Get the state variables as defined by `cbs` for a given location (`loc_id`)."
56
+ variables = self.run_cbs(loc_id)
57
+ if as_numpy:
58
+ return (np.array([v.name for v in variables]),
59
+ np.array([v.value for v in variables]))
60
+ else:
61
+ return variables
62
+
63
+ # %% ../nbs/04_callbacks.ipynb 10
64
+ @patch
65
+ def __call__(self:State, loc_id=None, **kwargs):
66
+ "Get the state variables as defined by `cbs` for all `loc_id`s as a dataframe."
67
+ loc_ids = self.smp_areas.index
68
+ results = [{v.name: v.value for v in self.run_cbs(loc_id)} | {'loc_id': loc_id} for loc_id in loc_ids]
69
+ return pd.DataFrame(results).set_index('loc_id')
70
+
71
+ # %% ../nbs/04_callbacks.ipynb 11
72
+ @patch
73
+ def expand_to_k_nearest(self:State,
74
+ subset_measurements:gpd.GeoDataFrame, # Measurements for which Variables are computed.
75
+ k:int=5, # Number of nearest neighbours (possibly belonging to adjacent cells/admin. units to consider).
76
+ ):
77
+ "Expand measurements of concern possibly to nearest neighbors of surrounding grid cells."
78
+ tree = KDTree(self.measurements.geometry.apply(lambda p: (p.x, p.y)).tolist());
79
+ _, indices = tree.query(subset_measurements.geometry.apply(lambda p: (p.x, p.y)).tolist(), k=k)
80
+ return self.measurements.iloc[indices.flatten()].reset_index(drop=True)
81
+
82
+ # %% ../nbs/04_callbacks.ipynb 12
83
+ @patch
84
+ def _flatten(self:State, variables):
85
+ "Flatten list of variables potentially containing both scalar and tuples."
86
+ return list(itertools.chain(*(v if isinstance(v, tuple) else (v,)
87
+ for v in variables)))
88
+
89
+ # %% ../nbs/04_callbacks.ipynb 13
90
+ @patch
91
+ def run_cbs(self:State, loc_id):
92
+ "Run Callbacks sequentially and flatten the results if required."
93
+ variables = []
94
+ for cb in self.cbs:
95
+ variables.append(cb(loc_id, self))
96
+ return self._flatten(variables)
97
+
98
+ # %% ../nbs/04_callbacks.ipynb 15
99
+ class MaxCB(Callback):
100
+ "Compute Maximum value of measurements at given location."
101
+ def __init__(self, name='Max'): fc.store_attr()
102
+ def __call__(self,
103
+ loc_id:int, # Unique id of an individual area of interest.
104
+ o:Type[State] # A State's object
105
+ ):
106
+ if loc_id in o.unsampled_locs: return Variable(self.name, np.nan)
107
+ return Variable(self.name,
108
+ np.max(o.measurements.loc[[loc_id]].value.values))
109
+
110
+ # %% ../nbs/04_callbacks.ipynb 16
111
+ class MinCB(Callback):
112
+ "Compute Minimum value of measurements at given location."
113
+ def __init__(self, name='Min'): fc.store_attr()
114
+ def __call__(self,
115
+ loc_id:int, # Unique id of an individual area of interest.
116
+ o:Type[State] # A State's object
117
+ ):
118
+ if loc_id in o.unsampled_locs: return Variable(self.name, np.nan)
119
+ return Variable(self.name,
120
+ np.min(o.measurements.loc[[loc_id]].value.values))
121
+
122
+ # %% ../nbs/04_callbacks.ipynb 17
123
+ class StdCB(Callback):
124
+ "Compute Standard deviation of measurements at given location."
125
+ def __init__(self, name='Standard Deviation'): fc.store_attr()
126
+ def __call__(self,
127
+ loc_id:int, # Unique id of an individual area of interest.
128
+ o:Type[State] # A State's object
129
+ ):
130
+ if loc_id in o.unsampled_locs: return Variable(self.name, np.nan)
131
+ return Variable(self.name,
132
+ np.std(o.measurements.loc[[loc_id]].value.values))
133
+
134
+ # %% ../nbs/04_callbacks.ipynb 18
135
+ class CountCB(Callback):
136
+ "Compute the number of measurements at given location."
137
+ def __init__(self, name='Count'): fc.store_attr()
138
+ def __call__(self,
139
+ loc_id:int, # Unique id of an individual area of interest.
140
+ o:Type[State] # A State's object
141
+ ):
142
+ if loc_id in o.unsampled_locs: return Variable(self.name, np.nan)
143
+ return Variable(self.name,
144
+ len(o.measurements.loc[[loc_id]].value.values))
145
+
146
+ # %% ../nbs/04_callbacks.ipynb 19
147
+ class MoranICB(Callback):
148
+ "Compute Moran.I of measurements at given location. Return NaN if p_sim above threshold."
149
+ def __init__(self, k=5, p_threshold=0.05, name='Moran.I', min_n=5): fc.store_attr()
150
+
151
+ def _weights(self, measurements):
152
+ w = weights.KNN.from_dataframe(measurements, k=self.k)
153
+ w.transform = "R" # Row-standardization
154
+ return w
155
+
156
+ def __call__(self,
157
+ loc_id:int, # Unique id of an individual area of interest.
158
+ o:Type[State] # A State's object
159
+ ):
160
+ if loc_id in o.unsampled_locs: return Variable(self.name, np.nan)
161
+ subset = o.measurements.loc[[loc_id]]
162
+ if len(subset) <= self.min_n: return Variable(self.name, np.nan)
163
+ expanded_measurements = o.expand_to_k_nearest(subset, k=self.k)
164
+ moran = esda.moran.Moran(expanded_measurements['value'], self._weights(expanded_measurements))
165
+ return Variable(self.name, moran.I if moran.p_sim < self.p_threshold else np.nan)
166
+
167
+ # %% ../nbs/04_callbacks.ipynb 20
168
+ class PriorCB(Callback):
169
+ "Emulate a prior by taking the mean of measurement over a single grid cell."
170
+ def __init__(self,
171
+ fname_raster:str, # Name of raster file
172
+ name:str='Prior' # Name of the State variable
173
+ ):
174
+ fc.store_attr()
175
+
176
+ def __call__(self,
177
+ loc_id:int, # Unique id of an individual area of interest.
178
+ o:Type[State] # A State's object
179
+ ):
180
+ polygon = o.smp_areas.loc[o.smp_areas.reset_index().loc_id == loc_id].geometry
181
+ with rasterio.open(self.fname_raster) as src:
182
+ out_image, out_transform = mask(src, polygon, crop=True)
183
+ mean_value = np.mean(out_image)
184
+ return Variable(self.name, mean_value)
trufl/collector.py ADDED
@@ -0,0 +1,35 @@
1
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/06_collector.ipynb.
2
+
3
+ # %% auto 0
4
+ __all__ = ['DataCollector']
5
+
6
+ # %% ../nbs/06_collector.ipynb 3
7
+ import rasterio
8
+ import fastcore.all as fc
9
+ import geopandas as gpd
10
+ from rasterio import transform
11
+
12
+ # %% ../nbs/06_collector.ipynb 5
13
+ class DataCollector:
14
+ def __init__(self,
15
+ fname_raster:str, # The path to the raster file.
16
+ band:int=1, # The band number to use. Defaults to 1.
17
+ ):
18
+ "Emulate data collection. Provided a set of location, return values sampled from given raster file."
19
+ fc.store_attr()
20
+ with rasterio.open(fname_raster) as src:
21
+ self.band_data = src.read(band)
22
+ self.affine = src.transform
23
+ self.bounds = src.bounds
24
+
25
+ def get_values(self,
26
+ gdf:gpd.GeoDataFrame # loc_id and Point/Multipoint geometry of samples where to measure.
27
+ ):
28
+ coords = [(x, y) for x, y in gdf.get_coordinates().values]
29
+ pixel_coords = [transform.rowcol(self.affine, *pair) for pair in coords]
30
+ return [self.band_data[int(x), int(y)] for (x, y) in pixel_coords]
31
+
32
+ def collect(self,
33
+ gdf:gpd.GeoDataFrame # loc_id and Point/Multipoint geometry of samples where to measure.
34
+ ) -> gpd.GeoDataFrame:
35
+ return gdf.explode(index_parts=False).assign(value=self.get_values(gdf))