anta-database 0.1.4__tar.gz → 0.2.0__tar.gz
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.
- {anta_database-0.1.4 → anta_database-0.2.0}/PKG-INFO +4 -1
- {anta_database-0.1.4 → anta_database-0.2.0}/README.md +1 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/__init__.py +1 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/anta_database.py +1 -0
- anta_database-0.2.0/anta_database/compiling/compile.py +262 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/database/database.py +31 -188
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/indexing/index_database.py +6 -13
- anta_database-0.2.0/anta_database/plotting/__init__.py +1 -0
- anta_database-0.2.0/anta_database/plotting/plotting.py +362 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/PKG-INFO +4 -1
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/SOURCES.txt +6 -4
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/requires.txt +1 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/book/_config.yml +1 -1
- {anta_database-0.1.4 → anta_database-0.2.0}/book/_toc.yml +2 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/book/advanced.md +26 -2
- anta_database-0.2.0/book/contact.md +9 -0
- anta_database-0.2.0/book/contribute.md +8 -0
- anta_database-0.2.0/book/figures/Overview_all.png +0 -0
- anta_database-0.2.0/book/intro.md +47 -0
- anta_database-0.2.0/book/quick_start.ipynb +559 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/pyproject.toml +2 -0
- anta_database-0.1.4/anta_database/compiling/compile.py +0 -199
- anta_database-0.1.4/anta_database/database/plotting/__init__.py +0 -1
- anta_database-0.1.4/book/figures/AntA_38ka.png +0 -0
- anta_database-0.1.4/book/figures/all_data.png +0 -0
- anta_database-0.1.4/book/intro.md +0 -38
- anta_database-0.1.4/book/quick_start.ipynb +0 -366
- {anta_database-0.1.4 → anta_database-0.2.0}/.github/workflows/deploy.yml +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/.github/workflows/release.yml +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/.gitignore +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/LICENSE +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/compiling/__init__.py +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/database/__init__.py +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/figures/AntA_38ka.png +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/indexing/__init__.py +0 -0
- {anta_database-0.1.4/anta_database/database → anta_database-0.2.0/anta_database}/plotting/GL.pkl +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/dependency_links.txt +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/top_level.txt +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/book/logo.png +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/book/references.bib +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/book/requirements.txt +0 -0
- {anta_database-0.1.4 → anta_database-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anta_database
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: SQLite database for the AntArchitecture Community Data
|
|
5
5
|
Author: Antoine Hermant
|
|
6
6
|
Author-email: antoine.hermant@etik.com
|
|
@@ -14,12 +14,14 @@ Classifier: Development Status :: 4 - Beta
|
|
|
14
14
|
Classifier: Intended Audience :: Science/Research
|
|
15
15
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
16
16
|
Classifier: Programming Language :: Python
|
|
17
|
+
Requires-Python: >=3.11
|
|
17
18
|
Description-Content-Type: text/markdown
|
|
18
19
|
License-File: LICENSE
|
|
19
20
|
Requires-Dist: pandas
|
|
20
21
|
Requires-Dist: pyproj
|
|
21
22
|
Requires-Dist: matplotlib
|
|
22
23
|
Requires-Dist: colormaps
|
|
24
|
+
Requires-Dist: tqdm
|
|
23
25
|
Dynamic: license-file
|
|
24
26
|
|
|
25
27
|
<h1 align="center">
|
|
@@ -27,6 +29,7 @@ Dynamic: license-file
|
|
|
27
29
|
</h1><br>
|
|
28
30
|
|
|
29
31
|
[](https://pypi.org/project/anta_database/)
|
|
32
|
+
[](https://anaconda.org/conda-forge/anta_database)
|
|
30
33
|
[](https://pypi.org/project/anta_database)
|
|
31
34
|
|
|
32
35
|
[](https://github.com/antoinehermant/anta_database/issues)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
</h1><br>
|
|
4
4
|
|
|
5
5
|
[](https://pypi.org/project/anta_database/)
|
|
6
|
+
[](https://anaconda.org/conda-forge/anta_database)
|
|
6
7
|
[](https://pypi.org/project/anta_database)
|
|
7
8
|
|
|
8
9
|
[](https://github.com/antoinehermant/anta_database/issues)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import numpy as np
|
|
5
|
+
import glob
|
|
6
|
+
from pyproj import Transformer
|
|
7
|
+
from typing import Union
|
|
8
|
+
from multiprocessing import Pool, cpu_count
|
|
9
|
+
from tqdm import tqdm
|
|
10
|
+
|
|
11
|
+
class CompileDatabase:
|
|
12
|
+
def __init__(self, dir_list: Union[str, list[str]], file_type: str = 'layer', wave_speed: Union[None, float] = None, firn_correction: Union[None, float] = None, comp: bool = True, post: bool = True) -> None:
|
|
13
|
+
self.dir_list = dir_list
|
|
14
|
+
self.wave_speed = wave_speed
|
|
15
|
+
self.firn_correction = firn_correction
|
|
16
|
+
self.file_type = file_type
|
|
17
|
+
self.comp = comp
|
|
18
|
+
self.post = post
|
|
19
|
+
|
|
20
|
+
def get_dict_ages(self, tab_file) -> dict:
|
|
21
|
+
ages = pd.read_csv(tab_file, header=None, sep='\t', names=['file', 'age'])
|
|
22
|
+
return dict(zip(ages['file'], ages['age']))
|
|
23
|
+
|
|
24
|
+
def _pre_compile_checks(self, dir_list: list[str]) -> bool:
|
|
25
|
+
missing = False
|
|
26
|
+
for dir_path in dir_list:
|
|
27
|
+
raw_dir = f"{dir_path}/raw/"
|
|
28
|
+
if not os.path.exists(raw_dir):
|
|
29
|
+
print(f"{raw_dir} does not exist")
|
|
30
|
+
missing = True
|
|
31
|
+
return not missing
|
|
32
|
+
|
|
33
|
+
def compile(self, cpus: int = cpu_count()-1) -> None:
|
|
34
|
+
if not isinstance(self.dir_list, list):
|
|
35
|
+
self.dir_list = [self.dir_list]
|
|
36
|
+
|
|
37
|
+
start_time = time.time()
|
|
38
|
+
if not self._pre_compile_checks(self.dir_list):
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
if self.comp is True:
|
|
42
|
+
all_files_list = []
|
|
43
|
+
for dir_ in self.dir_list:
|
|
44
|
+
files = glob.glob(f"{dir_}/raw/*.*")
|
|
45
|
+
for file_path in files:
|
|
46
|
+
all_files_list.append({
|
|
47
|
+
'dir_path': dir_,
|
|
48
|
+
'file': os.path.basename(file_path),
|
|
49
|
+
'file_path': file_path
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
num_tasks = len(all_files_list)
|
|
53
|
+
num_workers = min(num_tasks, cpus)
|
|
54
|
+
|
|
55
|
+
print('\n',
|
|
56
|
+
'Will start compiling', num_tasks, 'raw files\n'
|
|
57
|
+
'\n ', num_workers, 'worker(s) allocated out of', cpu_count(), 'available cpus\n')
|
|
58
|
+
|
|
59
|
+
if num_workers > 1:
|
|
60
|
+
with Pool(num_workers) as pool:
|
|
61
|
+
for _ in tqdm(pool.imap_unordered(self._compile, all_files_list), total=num_tasks):
|
|
62
|
+
pass
|
|
63
|
+
else:
|
|
64
|
+
for file_dict in tqdm(all_files_list, desc="Processing"):
|
|
65
|
+
self._compile(file_dict=file_dict)
|
|
66
|
+
|
|
67
|
+
if self.post is True:
|
|
68
|
+
all_dirs = []
|
|
69
|
+
for dir_ in self.dir_list:
|
|
70
|
+
dirs = [d for d in glob.glob(f"{dir_}/pkl/*") if os.path.isdir(d)]
|
|
71
|
+
all_dirs.extend(dirs)
|
|
72
|
+
|
|
73
|
+
num_tasks = len(all_dirs)
|
|
74
|
+
num_workers = min(num_tasks, cpus)
|
|
75
|
+
|
|
76
|
+
print('\n',
|
|
77
|
+
'Will start post compilation of', len(all_dirs), 'traces\n'
|
|
78
|
+
'\n ', num_workers, 'worker(s) allocated out of', cpu_count(), 'available cpus\n')
|
|
79
|
+
|
|
80
|
+
if num_workers > 1:
|
|
81
|
+
with Pool(num_workers) as pool:
|
|
82
|
+
for _ in tqdm(pool.imap_unordered(self._post_compilation, all_dirs), total=num_tasks):
|
|
83
|
+
pass
|
|
84
|
+
else:
|
|
85
|
+
for trace_dir in tqdm(all_dirs, desc='Processing'):
|
|
86
|
+
self._post_compilation(trace_dir=trace_dir)
|
|
87
|
+
|
|
88
|
+
elapsed = time.time() - start_time
|
|
89
|
+
print(f"\nCompilation completed in {elapsed:.2f} seconds")
|
|
90
|
+
|
|
91
|
+
def _compile(self, file_dict) -> None:
|
|
92
|
+
|
|
93
|
+
_, ext = os.path.splitext(file_dict['file'])
|
|
94
|
+
ages = self.get_dict_ages(f'{file_dict['dir_path']}/IRH_ages.tab')
|
|
95
|
+
original_new_columns = pd.read_csv(f'{file_dict['dir_path']}/original_new_column_names.csv')
|
|
96
|
+
|
|
97
|
+
if ext == '.tab':
|
|
98
|
+
sep='\t'
|
|
99
|
+
elif ext == '.csv':
|
|
100
|
+
sep=','
|
|
101
|
+
else:
|
|
102
|
+
print(f"{ext}: File type not supported...")
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
ds = pd.read_csv(file_dict['file_path'], comment="#", header=0, sep=sep, na_values=['-9999', 'NaN', ''])
|
|
106
|
+
_, file_name = os.path.split(file_dict['file_path'])
|
|
107
|
+
file_name_, ext = os.path.splitext(file_name)
|
|
108
|
+
|
|
109
|
+
ds = ds[ds.columns.intersection(original_new_columns.columns)]
|
|
110
|
+
ds.columns = original_new_columns[ds.columns].iloc[0].values # renaming the columns
|
|
111
|
+
|
|
112
|
+
if 'IceThk' in ds.columns and 'SurfElev' in ds.columns and not 'BedElev' in ds.columns:
|
|
113
|
+
ds['BedElev'] = ds['SurfElev'] - ds['IceThk']
|
|
114
|
+
if 'IceThk' in ds.columns and 'BedElev' in ds.columns and not 'SurfElev' in ds.columns:
|
|
115
|
+
ds['SurfElev'] = ds['BedElev'] + ds['IceThk']
|
|
116
|
+
if 'SurfElev' in ds.columns and 'BedElev' in ds.columns and not 'IceThk' in ds.columns:
|
|
117
|
+
ds['IceThk'] = ds['SurfElev'] - ds['BedElev']
|
|
118
|
+
|
|
119
|
+
if self.wave_speed:
|
|
120
|
+
for var in ['IceThk', 'BedElev']:
|
|
121
|
+
if var in ds.columns:
|
|
122
|
+
ds[var] *= self.wave_speed
|
|
123
|
+
if self.firn_correction:
|
|
124
|
+
for var in ['IceThk', 'BedElev']:
|
|
125
|
+
if var in ds.columns:
|
|
126
|
+
ds[var] += self.firn_correction
|
|
127
|
+
|
|
128
|
+
if 'x' not in ds.columns and 'y' not in ds.columns:
|
|
129
|
+
if 'lon' in ds.columns and 'lat' in ds.columns:
|
|
130
|
+
transformer = Transformer.from_proj(
|
|
131
|
+
"EPSG:4326", # source: WGS84 (lon/lat)
|
|
132
|
+
"+proj=stere +lon_0=0 +lat_0=-90 +lat_ts=-71 +datum=WGS84 +units=m +no_defs", # target: polar
|
|
133
|
+
always_xy=True
|
|
134
|
+
)
|
|
135
|
+
ds['x'], ds['y'] = transformer.transform(ds['lon'].values, ds['lat'].values)
|
|
136
|
+
elif 'lon' not in ds.columns and 'lat' not in ds.columns:
|
|
137
|
+
if 'x' in ds.columns and 'y' in ds.columns:
|
|
138
|
+
inverse_transformer = Transformer.from_proj(
|
|
139
|
+
"+proj=stere +lon_0=0 +lat_0=-90 +lat_ts=-71 +datum=WGS84 +units=m +no_defs", # source: polar
|
|
140
|
+
"EPSG:4326", # target: WGS84 (lon/lat)
|
|
141
|
+
always_xy=True
|
|
142
|
+
)
|
|
143
|
+
ds['lon'], ds['lat'] = inverse_transformer.transform(ds['x'].values, ds['y'].values)
|
|
144
|
+
elif 'lon' in ds.columns and 'lat' in ds.columns and 'x' in ds.columns and 'y' in ds.columns:
|
|
145
|
+
pass
|
|
146
|
+
else:
|
|
147
|
+
print('No coordinates found in the dataset')
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
if self.file_type == 'layer':
|
|
151
|
+
age = str(ages[file_name_])
|
|
152
|
+
if self.wave_speed:
|
|
153
|
+
ds['IRHDepth'] *= self.wave_speed
|
|
154
|
+
if self.firn_correction:
|
|
155
|
+
ds['IRHDepth'] += self.firn_correction
|
|
156
|
+
|
|
157
|
+
ds['Trace_ID'] = ds['Trace_ID'].astype(str)
|
|
158
|
+
ds['Trace_ID'] = ds['Trace_ID'].str.replace(r'/\s+', '_') # Replace slashes with underscores, otherwise the paths can get messy
|
|
159
|
+
ds['Trace_ID'] = ds['Trace_ID'].str.replace('/', '_')
|
|
160
|
+
|
|
161
|
+
ds.set_index('Trace_ID', inplace=True)
|
|
162
|
+
|
|
163
|
+
for trace_id in np.unique(ds.index):
|
|
164
|
+
ds_trace = ds.loc[trace_id].copy()
|
|
165
|
+
ds_trace = ds_trace.drop_duplicates(subset=['x', 'y']) # Some datasets showed duplicated data
|
|
166
|
+
if 'distance' not in ds_trace.columns:
|
|
167
|
+
x = ds_trace[['x', 'y']]
|
|
168
|
+
distances = np.sqrt(np.sum(np.diff(x, axis=0)**2, axis=1))
|
|
169
|
+
cumulative_distance = np.concatenate([[0], np.cumsum(distances)])
|
|
170
|
+
ds_trace['distance'] = cumulative_distance
|
|
171
|
+
elif 'Distance [km]' in original_new_columns.columns:
|
|
172
|
+
ds_trace['distance'] *= 1000 # if distance in km, convert to meters
|
|
173
|
+
|
|
174
|
+
if age in ['IceThk', 'BedElev', 'SurfElev', 'BasalUnit']:
|
|
175
|
+
ds_trace = ds_trace.rename(columns={'IRHDepth': age})
|
|
176
|
+
ds_trace_file = f'{file_dict['dir_path']}/pkl/{trace_id}/{age}.pkl' # if var instead of age, call the file as var.pkl
|
|
177
|
+
else:
|
|
178
|
+
ds_trace_file = f'{file_dict['dir_path']}/pkl/{trace_id}/{file_name_}.pkl' # else use the same file name.pkl
|
|
179
|
+
|
|
180
|
+
os.makedirs(f'{file_dict['dir_path']}/pkl/{trace_id}' , exist_ok=True)
|
|
181
|
+
ds_trace.to_pickle(ds_trace_file)
|
|
182
|
+
|
|
183
|
+
elif self.file_type == 'trace':
|
|
184
|
+
if 'distance' not in ds.columns:
|
|
185
|
+
x = ds[['x', 'y']]
|
|
186
|
+
distances = np.sqrt(np.sum(np.diff(x, axis=0)**2, axis=1))
|
|
187
|
+
cumulative_distance = np.concatenate([[0], np.cumsum(distances)])
|
|
188
|
+
ds['distance'] = cumulative_distance
|
|
189
|
+
elif 'Distance [km]' in original_new_columns.columns:
|
|
190
|
+
ds['distance'] *= 1000 # if distance in km, convert to meters
|
|
191
|
+
|
|
192
|
+
trace_id = file_name_
|
|
193
|
+
os.makedirs(f'{file_dict['dir_path']}/pkl/{trace_id}' , exist_ok=True)
|
|
194
|
+
|
|
195
|
+
ages = {key: ages[key] for key in ds.columns if key in ages}
|
|
196
|
+
|
|
197
|
+
for IRH in ages:
|
|
198
|
+
age = str(ages.get(IRH))
|
|
199
|
+
ds_IRH = ds[IRH]
|
|
200
|
+
ds_IRH = pd.DataFrame({
|
|
201
|
+
'lon': ds['lon'],
|
|
202
|
+
'lat': ds['lat'],
|
|
203
|
+
'x': ds['x'],
|
|
204
|
+
'y': ds['y'],
|
|
205
|
+
'distance': ds['distance'],
|
|
206
|
+
'IRHDepth': ds_IRH,
|
|
207
|
+
})
|
|
208
|
+
if self.wave_speed:
|
|
209
|
+
ds_IRH['IRHDepth'] *= self.wave_speed
|
|
210
|
+
if self.firn_correction:
|
|
211
|
+
ds_IRH['IRHDepth'] += self.firn_correction
|
|
212
|
+
|
|
213
|
+
for var in ['IceThk', 'BedElev', 'SurfElev', 'BasalUnit']:
|
|
214
|
+
if var in ds.columns:
|
|
215
|
+
ds_IRH[var] = ds[var]
|
|
216
|
+
|
|
217
|
+
ds_trace_file = f'{file_dict['dir_path']}/pkl/{trace_id}/{IRH}.pkl'
|
|
218
|
+
ds_IRH.to_pickle(ds_trace_file)
|
|
219
|
+
|
|
220
|
+
def compute_irh_density(self, trace_dir) -> None:
|
|
221
|
+
files = glob.glob(f"{trace_dir}/*.pkl")
|
|
222
|
+
|
|
223
|
+
unwanted = {'IceThk.pkl', 'SurfElev.pkl', 'BasalUnit.pkl', 'BedElev.pkl', 'IRHDensity.pkl'}
|
|
224
|
+
files = [f for f in glob.glob(f"{trace_dir}/*.pkl") if os.path.basename(f) not in unwanted]
|
|
225
|
+
if len(files) > 1:
|
|
226
|
+
dfs = [pd.read_pickle(f) for f in files]
|
|
227
|
+
dfs = pd.concat(dfs)
|
|
228
|
+
elif len(files) == 1:
|
|
229
|
+
dfs = pd.read_pickle(files[0])
|
|
230
|
+
else:
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
dfs = dfs[['x','y','IRHDepth']]
|
|
234
|
+
valid = dfs.dropna(subset=['IRHDepth'])
|
|
235
|
+
density = valid.groupby(['x', 'y']).size().reset_index(name='IRHDensity')
|
|
236
|
+
|
|
237
|
+
density_file = f'{trace_dir}/IRHDensity.pkl'
|
|
238
|
+
density.to_pickle(density_file)
|
|
239
|
+
|
|
240
|
+
def extract_vars(self, trace_dir: str) -> None:
|
|
241
|
+
files = glob.glob(f"{trace_dir}/*.pkl")
|
|
242
|
+
|
|
243
|
+
unwanted = {'IceThk.pkl', 'SurfElev.pkl', 'BasalUnit.pkl', 'BedElev.pkl', 'IRHDensity.pkl'}
|
|
244
|
+
files = [f for f in glob.glob(f"{trace_dir}/*.pkl") if os.path.basename(f) not in unwanted]
|
|
245
|
+
if len(files) > 1:
|
|
246
|
+
dfs = [pd.read_pickle(f) for f in files]
|
|
247
|
+
dfs = pd.concat(dfs).drop_duplicates(subset=['x', 'y'])
|
|
248
|
+
elif len(files) == 1:
|
|
249
|
+
dfs = pd.read_pickle(files[0])
|
|
250
|
+
else:
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
for var in ['IceThk', 'BedElev', 'SurfElev']:
|
|
254
|
+
if var in dfs.columns:
|
|
255
|
+
ds_var = dfs[['x', 'y', 'distance', var]]
|
|
256
|
+
var_file = f'{trace_dir}/{var}.pkl'
|
|
257
|
+
ds_var.to_pickle(var_file)
|
|
258
|
+
|
|
259
|
+
def _post_compilation(self, trace_dir: str) -> None:
|
|
260
|
+
self.extract_vars(trace_dir)
|
|
261
|
+
self.compute_irh_density(trace_dir)
|
|
262
|
+
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import os
|
|
1
|
+
import os
|
|
2
2
|
import sqlite3
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import numpy as np
|
|
5
|
-
import matplotlib.pyplot as plt
|
|
6
|
-
from matplotlib.colors import LinearSegmentedColormap
|
|
7
|
-
import colormaps as cmaps
|
|
8
|
-
from contextlib import contextmanager
|
|
9
5
|
from typing import Union, List, Dict, Tuple
|
|
10
6
|
|
|
7
|
+
from anta_database.plotting.plotting import Plotting
|
|
8
|
+
|
|
11
9
|
class Database:
|
|
12
|
-
def __init__(self, database_dir: str, file_db: str = 'AntADatabase.db'):
|
|
13
|
-
module_dir = os.path.dirname(os.path.abspath(__file__))
|
|
14
|
-
self.gl = pd.read_pickle(os.path.join(module_dir, 'plotting', 'GL.pkl'))
|
|
10
|
+
def __init__(self, database_dir: str, file_db: str = 'AntADatabase.db') -> None:
|
|
15
11
|
self.db_dir = database_dir
|
|
16
12
|
self.file_db = file_db
|
|
17
13
|
self.file_db_path = os.path.join(self.db_dir, file_db)
|
|
18
14
|
self.md = None
|
|
15
|
+
self._plotting = None
|
|
19
16
|
|
|
20
17
|
def _build_query_and_params(self, age: Union[None, str, List[str]]=None, var: Union[None, str, List[str]]=None, author: Union[None, str, List[str]]=None, line: Union[None, str, List[str]]=None, select_clause='') -> Tuple[str, List[Union[str, int]]]:
|
|
21
18
|
"""
|
|
@@ -29,7 +26,6 @@ class Database:
|
|
|
29
26
|
'''
|
|
30
27
|
conditions = []
|
|
31
28
|
params = []
|
|
32
|
-
|
|
33
29
|
for field, column in [
|
|
34
30
|
(age, 'd.age'),
|
|
35
31
|
(var, 'd.var'),
|
|
@@ -38,17 +34,31 @@ class Database:
|
|
|
38
34
|
]:
|
|
39
35
|
if field is not None:
|
|
40
36
|
if isinstance(field, list):
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
# For lists, use IN for exact matches, or LIKE for wildcards
|
|
38
|
+
like_conditions = []
|
|
39
|
+
in_values = []
|
|
40
|
+
for item in field:
|
|
41
|
+
if '%' in item:
|
|
42
|
+
like_conditions.append(f"{column} LIKE ?")
|
|
43
|
+
params.append(item)
|
|
44
|
+
else:
|
|
45
|
+
in_values.append(item)
|
|
46
|
+
if like_conditions:
|
|
47
|
+
conditions.append('(' + ' OR '.join(like_conditions) + ')')
|
|
48
|
+
if in_values:
|
|
49
|
+
placeholders = ','.join(['?'] * len(in_values))
|
|
50
|
+
conditions.append(f"{column} IN ({placeholders})")
|
|
51
|
+
params.extend(in_values)
|
|
44
52
|
else:
|
|
45
|
-
|
|
53
|
+
# For single values, use = or LIKE
|
|
54
|
+
if '%' in field:
|
|
55
|
+
conditions.append(f"{column} LIKE ?")
|
|
56
|
+
else:
|
|
57
|
+
conditions.append(f"{column} = ?")
|
|
46
58
|
params.append(field)
|
|
47
|
-
|
|
48
59
|
if conditions:
|
|
49
60
|
query += ' WHERE ' + ' AND '.join(conditions)
|
|
50
|
-
|
|
51
|
-
query += 'ORDER BY CAST(d.age AS INTEGER) ASC'
|
|
61
|
+
query += ' ORDER BY CAST(d.age AS INTEGER) ASC'
|
|
52
62
|
return query, params
|
|
53
63
|
|
|
54
64
|
def _get_file_metadata(self, file_path) -> Dict:
|
|
@@ -207,179 +217,12 @@ class Database:
|
|
|
207
217
|
metadata = self._get_file_metadata(file_path)
|
|
208
218
|
yield df, metadata
|
|
209
219
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
cm1_colors = cm1(np.linspace(0.05, 0.8, 256)) # Shape: (256, 4) RGBA
|
|
216
|
-
cm2_colors = cm2(np.linspace(0.1, 1, 256)) # Shape: (256, 4) RGBA
|
|
217
|
-
combined_colors = np.vstack((cm1_colors, cm2_colors))
|
|
218
|
-
custom_cmap = LinearSegmentedColormap.from_list(
|
|
219
|
-
'custom_cmap', # Name your colormap
|
|
220
|
-
combined_colors,
|
|
221
|
-
N=512 # Double the resolution (256 + 256)
|
|
222
|
-
)
|
|
223
|
-
return custom_cmap
|
|
224
|
-
|
|
225
|
-
def is_notebook(self) -> bool:
|
|
226
|
-
from IPython import get_ipython
|
|
227
|
-
try:
|
|
228
|
-
shell = get_ipython().__class__.__name__
|
|
229
|
-
if shell == 'ZMQInteractiveShell':
|
|
230
|
-
return True # Jupyter notebook or qtconsole
|
|
231
|
-
elif shell == 'TerminalInteractiveShell':
|
|
232
|
-
return False # Terminal running IPython
|
|
233
|
-
else:
|
|
234
|
-
return False # Other type (?)
|
|
235
|
-
except NameError:
|
|
236
|
-
return False # Probably standard Python interpreter
|
|
237
|
-
|
|
238
|
-
@contextmanager
|
|
239
|
-
def plot_context(self, close=None):
|
|
240
|
-
if close is None:
|
|
241
|
-
close = not self.is_notebook()
|
|
242
|
-
try:
|
|
243
|
-
yield
|
|
244
|
-
finally:
|
|
245
|
-
if close:
|
|
246
|
-
plt.close()
|
|
247
|
-
|
|
248
|
-
def plotXY(self,
|
|
249
|
-
metadata: Union[None, Dict, 'MetadataResult'] = None,
|
|
250
|
-
downscale_factor: Union[None, int] = None,
|
|
251
|
-
title: str = '',
|
|
252
|
-
xlim: tuple = (None, None),
|
|
253
|
-
ylim: tuple = (None, None),
|
|
254
|
-
scale_factor: float = 1.0,
|
|
255
|
-
latex:bool = False,
|
|
256
|
-
save: Union[str, None] = None) -> None:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if metadata:
|
|
260
|
-
metadata = metadata
|
|
261
|
-
elif self.md:
|
|
262
|
-
metadata = self.md
|
|
263
|
-
else:
|
|
264
|
-
print('Please provide metadata of the files you want to generate the data from...')
|
|
265
|
-
return
|
|
266
|
-
|
|
267
|
-
cmaps = self.custom_cmap()
|
|
268
|
-
authors = list(metadata['author'])
|
|
269
|
-
color_indices = np.linspace(0.1, 0.9, len(authors))
|
|
270
|
-
colors = {author: cmaps(i) for i, author in zip(color_indices, authors)}
|
|
271
|
-
|
|
272
|
-
if latex:
|
|
273
|
-
from matplotlib import rc
|
|
274
|
-
rc('font', **{'family': 'serif', 'serif': ['Computer Modern']})
|
|
275
|
-
rc('text', usetex=True)
|
|
276
|
-
|
|
277
|
-
plt.figure()
|
|
278
|
-
plt.plot(self.gl.x/1000, self.gl.y/1000, linewidth=1, color='k')
|
|
279
|
-
for df, md in self.data_generator(metadata, downscale_factor=downscale_factor):
|
|
280
|
-
author = md['author']
|
|
281
|
-
plt.plot(df.x/1000, df.y/1000, linewidth=0.8, color=colors[author])
|
|
282
|
-
|
|
283
|
-
ax = plt.gca()
|
|
284
|
-
x0, x1 = ax.get_xlim() if xlim == (None, None) else xlim
|
|
285
|
-
y0, y1 = ax.get_ylim() if ylim == (None, None) else ylim
|
|
286
|
-
|
|
287
|
-
for author in authors:
|
|
288
|
-
citation = self.query(author=author)['reference']
|
|
289
|
-
plt.plot([], [], color=colors[author], label=citation)
|
|
290
|
-
plt.legend(loc='lower left', fontsize=16*scale_factor)
|
|
291
|
-
|
|
292
|
-
x_extent = x1 - x0
|
|
293
|
-
y_extent = y1 - y0
|
|
220
|
+
@property
|
|
221
|
+
def plot(self):
|
|
222
|
+
if self._plotting is None:
|
|
223
|
+
self._plotting = Plotting(self)
|
|
224
|
+
return self._plotting
|
|
294
225
|
|
|
295
|
-
aspect_ratio = y_extent / x_extent
|
|
296
|
-
|
|
297
|
-
# Set the figure size based on the aspect ratio and a scale factor
|
|
298
|
-
plt.gcf().set_size_inches(8 * scale_factor, 8 * aspect_ratio * scale_factor)
|
|
299
|
-
ax.set_xlabel('x [km]')
|
|
300
|
-
ax.set_ylabel('y [km]')
|
|
301
|
-
ax.set_xlim(*xlim)
|
|
302
|
-
ax.set_ylim(*ylim)
|
|
303
|
-
# self.scale_axes(ax, factor=1000)
|
|
304
|
-
plt.title(title, fontsize=30*scale_factor)
|
|
305
|
-
plt.tight_layout()
|
|
306
|
-
|
|
307
|
-
with self.plot_context():
|
|
308
|
-
|
|
309
|
-
if save:
|
|
310
|
-
plt.savefig(save, dpi=200)
|
|
311
|
-
print('Figure saved as', save)
|
|
312
|
-
else:
|
|
313
|
-
plt.show()
|
|
314
|
-
|
|
315
|
-
def plotXYDepth(self,
|
|
316
|
-
metadata: Union[None, Dict, 'MetadataResult'] = None,
|
|
317
|
-
downscale_factor: Union[None, int] = None,
|
|
318
|
-
title: str = '',
|
|
319
|
-
xlim: tuple = (None, None),
|
|
320
|
-
ylim: tuple = (None, None),
|
|
321
|
-
scale_factor: float = 1.0,
|
|
322
|
-
latex:bool = False,
|
|
323
|
-
cmaps = cmaps.torch_r,
|
|
324
|
-
save: Union[str, None] = None) -> None:
|
|
325
|
-
|
|
326
|
-
if metadata:
|
|
327
|
-
metadata = metadata
|
|
328
|
-
elif self.md:
|
|
329
|
-
metadata = self.md
|
|
330
|
-
else:
|
|
331
|
-
print('Please provide metadata of the files you want to generate the data from...')
|
|
332
|
-
return
|
|
333
|
-
|
|
334
|
-
if latex:
|
|
335
|
-
from matplotlib import rc
|
|
336
|
-
rc('font', **{'family': 'serif', 'serif': ['Computer Modern']})
|
|
337
|
-
rc('text', usetex=True)
|
|
338
|
-
|
|
339
|
-
fig, ax = plt.subplots(figsize=(10,6))
|
|
340
|
-
plt.plot(self.gl.x/1000, self.gl.y/1000, linewidth=1, color='k')
|
|
341
|
-
for df, md in self.data_generator(metadata, downscale_factor=downscale_factor):
|
|
342
|
-
if md['age']:
|
|
343
|
-
var = md['age']
|
|
344
|
-
label = f'{var} isochrone depth [m]'
|
|
345
|
-
elif md['var']:
|
|
346
|
-
var = md['var']
|
|
347
|
-
label = f'{var} [m]'
|
|
348
|
-
if 'var' in locals():
|
|
349
|
-
scatter = plt.scatter(df['x']/1000, df['y']/1000, c=df[var], cmap=cmaps, s=1)
|
|
350
|
-
|
|
351
|
-
x0, x1 = ax.get_xlim() if xlim == (None, None) else xlim
|
|
352
|
-
y0, y1 = ax.get_ylim() if ylim == (None, None) else ylim
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
x_extent = x1 - x0
|
|
356
|
-
y_extent = y1 - y0
|
|
357
|
-
|
|
358
|
-
aspect_ratio = y_extent / x_extent
|
|
359
|
-
|
|
360
|
-
# Set the figure size based on the aspect ratio and a scale factor
|
|
361
|
-
plt.gcf().set_size_inches(8 * scale_factor, 8 * aspect_ratio * scale_factor)
|
|
362
|
-
ax.set_xlabel('x [km]')
|
|
363
|
-
ax.set_ylabel('y [km]')
|
|
364
|
-
ax.set_xlim(*xlim)
|
|
365
|
-
ax.set_ylim(*ylim)
|
|
366
|
-
# self.scale_axes(ax, factor=1000)
|
|
367
|
-
plt.title(title, fontsize=30*scale_factor)
|
|
368
|
-
|
|
369
|
-
if 'scatter' in locals():
|
|
370
|
-
cbar = fig.colorbar(scatter, ax=ax, orientation='horizontal', pad=0.05, fraction=0.05)
|
|
371
|
-
cbar.ax.xaxis.set_ticks_position('bottom')
|
|
372
|
-
if 'label' in locals():
|
|
373
|
-
cbar.set_label(label)
|
|
374
|
-
|
|
375
|
-
plt.tight_layout()
|
|
376
|
-
|
|
377
|
-
with self.plot_context():
|
|
378
|
-
if save:
|
|
379
|
-
plt.savefig(save, dpi=200)
|
|
380
|
-
print('Figure saved as', save)
|
|
381
|
-
else:
|
|
382
|
-
plt.show()
|
|
383
226
|
|
|
384
227
|
class MetadataResult:
|
|
385
228
|
def __init__(self, metadata):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from tqdm import tqdm
|
|
2
3
|
import glob
|
|
3
4
|
import pandas as pd
|
|
4
5
|
import sqlite3
|
|
@@ -14,20 +15,13 @@ class IndexDatabase:
|
|
|
14
15
|
ages = pd.read_csv(tab_file, header=None, sep='\t', names=['file', 'age'])
|
|
15
16
|
return dict(zip(ages['file'], ages['age']))
|
|
16
17
|
|
|
17
|
-
def get_dict_vars(self, tab_file) -> dict:
|
|
18
|
-
ages = pd.read_csv(tab_file, header=None, sep='\t', names=['file', 'var'])
|
|
19
|
-
return dict(zip(ages['file'], ages['var']))
|
|
20
|
-
|
|
21
18
|
def index_database(self):
|
|
22
19
|
Authors_ages = {}
|
|
23
20
|
for _, row in self.index.iterrows():
|
|
24
21
|
ages = self.get_dict_ages(f"{self.db_dir}/{row.directory}/IRH_ages.tab")
|
|
25
22
|
Authors_ages.update({f"{row.directory}": ages})
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
for _, row in self.index.iterrows():
|
|
29
|
-
vars = self.get_dict_ages(f"{self.db_dir}/{row.directory}/vars.tab")
|
|
30
|
-
Extra_vars.update({f"{row.directory}": vars})
|
|
24
|
+
var_list = pd.read_csv(f"{self.db_dir}/vars.csv").columns
|
|
31
25
|
|
|
32
26
|
if os.path.exists(self.file_db):
|
|
33
27
|
os.remove(self.file_db)
|
|
@@ -67,8 +61,8 @@ class IndexDatabase:
|
|
|
67
61
|
)
|
|
68
62
|
''')
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
for file in
|
|
64
|
+
pkl_files = list(glob.glob(f'{self.db_dir}/**/**/*.pkl', recursive=True))
|
|
65
|
+
for file in tqdm(pkl_files, desc="Processing files"):
|
|
72
66
|
dir_name, file_name = os.path.split(file)
|
|
73
67
|
pkl_dir, trace_id = os.path.split(dir_name)
|
|
74
68
|
author_dir, _ = os.path.split(pkl_dir)
|
|
@@ -76,7 +70,6 @@ class IndexDatabase:
|
|
|
76
70
|
author = os.path.basename(author_dir)
|
|
77
71
|
file_name_, ext = os.path.splitext(file_name)
|
|
78
72
|
relative_file_path = f'{author}/pkl/{trace_id}/{file_name}'
|
|
79
|
-
print(relative_file_path)
|
|
80
73
|
|
|
81
74
|
# Get the author's ID from the authors table
|
|
82
75
|
cursor.execute('SELECT id FROM authors WHERE name = ?', (author,))
|
|
@@ -87,8 +80,8 @@ class IndexDatabase:
|
|
|
87
80
|
else:
|
|
88
81
|
age = None
|
|
89
82
|
|
|
90
|
-
if file_name_ in
|
|
91
|
-
var =
|
|
83
|
+
if file_name_ in var_list:
|
|
84
|
+
var = file_name_
|
|
92
85
|
else:
|
|
93
86
|
var = None
|
|
94
87
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .plotting import *
|