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.
Files changed (42) hide show
  1. {anta_database-0.1.4 → anta_database-0.2.0}/PKG-INFO +4 -1
  2. {anta_database-0.1.4 → anta_database-0.2.0}/README.md +1 -0
  3. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/__init__.py +1 -0
  4. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/anta_database.py +1 -0
  5. anta_database-0.2.0/anta_database/compiling/compile.py +262 -0
  6. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/database/database.py +31 -188
  7. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/indexing/index_database.py +6 -13
  8. anta_database-0.2.0/anta_database/plotting/__init__.py +1 -0
  9. anta_database-0.2.0/anta_database/plotting/plotting.py +362 -0
  10. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/PKG-INFO +4 -1
  11. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/SOURCES.txt +6 -4
  12. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/requires.txt +1 -0
  13. {anta_database-0.1.4 → anta_database-0.2.0}/book/_config.yml +1 -1
  14. {anta_database-0.1.4 → anta_database-0.2.0}/book/_toc.yml +2 -0
  15. {anta_database-0.1.4 → anta_database-0.2.0}/book/advanced.md +26 -2
  16. anta_database-0.2.0/book/contact.md +9 -0
  17. anta_database-0.2.0/book/contribute.md +8 -0
  18. anta_database-0.2.0/book/figures/Overview_all.png +0 -0
  19. anta_database-0.2.0/book/intro.md +47 -0
  20. anta_database-0.2.0/book/quick_start.ipynb +559 -0
  21. {anta_database-0.1.4 → anta_database-0.2.0}/pyproject.toml +2 -0
  22. anta_database-0.1.4/anta_database/compiling/compile.py +0 -199
  23. anta_database-0.1.4/anta_database/database/plotting/__init__.py +0 -1
  24. anta_database-0.1.4/book/figures/AntA_38ka.png +0 -0
  25. anta_database-0.1.4/book/figures/all_data.png +0 -0
  26. anta_database-0.1.4/book/intro.md +0 -38
  27. anta_database-0.1.4/book/quick_start.ipynb +0 -366
  28. {anta_database-0.1.4 → anta_database-0.2.0}/.github/workflows/deploy.yml +0 -0
  29. {anta_database-0.1.4 → anta_database-0.2.0}/.github/workflows/release.yml +0 -0
  30. {anta_database-0.1.4 → anta_database-0.2.0}/.gitignore +0 -0
  31. {anta_database-0.1.4 → anta_database-0.2.0}/LICENSE +0 -0
  32. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/compiling/__init__.py +0 -0
  33. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/database/__init__.py +0 -0
  34. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/figures/AntA_38ka.png +0 -0
  35. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database/indexing/__init__.py +0 -0
  36. {anta_database-0.1.4/anta_database/database → anta_database-0.2.0/anta_database}/plotting/GL.pkl +0 -0
  37. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/dependency_links.txt +0 -0
  38. {anta_database-0.1.4 → anta_database-0.2.0}/anta_database.egg-info/top_level.txt +0 -0
  39. {anta_database-0.1.4 → anta_database-0.2.0}/book/logo.png +0 -0
  40. {anta_database-0.1.4 → anta_database-0.2.0}/book/references.bib +0 -0
  41. {anta_database-0.1.4 → anta_database-0.2.0}/book/requirements.txt +0 -0
  42. {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.1.4
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
  [![PyPi](https://img.shields.io/pypi/v/anta_database)](https://pypi.org/project/anta_database/)
32
+ [![Conda](https://img.shields.io/conda/vn/conda-forge/anta_database)](https://anaconda.org/conda-forge/anta_database)
30
33
  [![Downloads](https://img.shields.io/pypi/dm/anta_database)](https://pypi.org/project/anta_database)
31
34
 
32
35
  [![GitHub issues](https://img.shields.io/badge/issue_tracking-github-blue.svg)](https://github.com/antoinehermant/anta_database/issues)
@@ -3,6 +3,7 @@
3
3
  </h1><br>
4
4
 
5
5
  [![PyPi](https://img.shields.io/pypi/v/anta_database)](https://pypi.org/project/anta_database/)
6
+ [![Conda](https://img.shields.io/conda/vn/conda-forge/anta_database)](https://anaconda.org/conda-forge/anta_database)
6
7
  [![Downloads](https://img.shields.io/pypi/dm/anta_database)](https://pypi.org/project/anta_database)
7
8
 
8
9
  [![GitHub issues](https://img.shields.io/badge/issue_tracking-github-blue.svg)](https://github.com/antoinehermant/anta_database/issues)
@@ -1,3 +1,4 @@
1
1
  from .database import *
2
2
  from .indexing import *
3
3
  from .compiling import *
4
+ from .plotting import *
@@ -1,3 +1,4 @@
1
1
  from database import *
2
2
  from indexing import *
3
3
  from compiling import *
4
+ from plotting import *
@@ -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, sys
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
- placeholders = ','.join(['?'] * len(field))
42
- conditions.append(f'{column} IN ({placeholders})')
43
- params.extend(field)
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
- conditions.append(f'{column} = ?')
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
- def custom_cmap(self):
211
-
212
- cm1 = cmaps.torch_r
213
- cm2 = cmaps.deep_r
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
- Extra_vars = {}
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
- # Insert the metadata for each dataset into the table
71
- for file in glob.glob(f'{self.db_dir}/**/**/*.pkl', recursive=True):
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 Extra_vars[author]:
91
- var = Extra_vars[author][file_name_]
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 *