seabirdfilehandler 0.4.0__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.
Potentially problematic release.
This version of seabirdfilehandler might be problematic. Click here for more details.
- seabirdfilehandler/__init__.py +5 -0
- seabirdfilehandler/dataframe_meta_accessor.py +184 -0
- seabirdfilehandler/datatablefiles.py +886 -0
- seabirdfilehandler/file_collection.py +269 -0
- seabirdfilehandler/logging.yaml +23 -0
- seabirdfilehandler/parameter.py +410 -0
- seabirdfilehandler/seabirdfiles.py +200 -0
- seabirdfilehandler/validation_modules.py +152 -0
- seabirdfilehandler/xmlfiles.py +87 -0
- seabirdfilehandler-0.4.0.dist-info/LICENSE +373 -0
- seabirdfilehandler-0.4.0.dist-info/METADATA +29 -0
- seabirdfilehandler-0.4.0.dist-info/RECORD +13 -0
- seabirdfilehandler-0.4.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import logging
|
|
3
|
+
from pandas.api.extensions import register_series_accessor
|
|
4
|
+
from pandas.api.extensions import register_dataframe_accessor
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MetadataHandler:
|
|
12
|
+
"""
|
|
13
|
+
The base class for the pandas series and dataframe accessors.
|
|
14
|
+
Offers a very basic metadata handling, by using a dictionary as metadata
|
|
15
|
+
store. The accessors then allow to access this metadata store and
|
|
16
|
+
corresponding methods by calling 'df.meta' or 'series.meta', respectively.
|
|
17
|
+
Mainly targeted for usage with dataframes featuring data from CNV files,
|
|
18
|
+
it for example allows the attachement of parameter metadata found in the
|
|
19
|
+
CNV header to individual dataframe columns.
|
|
20
|
+
|
|
21
|
+
This approach was chosen over others, like directly subclassing the pandas
|
|
22
|
+
dataframe or series class, or a seperate metadata storage, due to its
|
|
23
|
+
simplicity and ability to keep using the full powerfull pandas library
|
|
24
|
+
without the need to implement each and every transformation. Of course,
|
|
25
|
+
the 'attrs' attribute does offer a similar metadata storage. But at the
|
|
26
|
+
time of writing this, it is still in a very experimental condition and does
|
|
27
|
+
not propagate reliably.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, pandas_obj):
|
|
31
|
+
self._obj = pandas_obj
|
|
32
|
+
if not hasattr(self._obj, "_metadata_store"):
|
|
33
|
+
with warnings.catch_warnings():
|
|
34
|
+
warnings.simplefilter("ignore")
|
|
35
|
+
self._obj._metadata_store = {}
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def metadata(self):
|
|
39
|
+
return self._obj._metadata_store
|
|
40
|
+
|
|
41
|
+
@metadata.setter
|
|
42
|
+
def metadata(self, value):
|
|
43
|
+
self._obj._metadata_store = value
|
|
44
|
+
|
|
45
|
+
def get(self, key, default=None):
|
|
46
|
+
return self._obj._metadata_store.get(key, default)
|
|
47
|
+
|
|
48
|
+
def set(self, key, value):
|
|
49
|
+
self._obj._metadata_store[key] = value
|
|
50
|
+
|
|
51
|
+
def clear(self):
|
|
52
|
+
self._obj._metadata_store.clear()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@register_series_accessor("meta")
|
|
56
|
+
class SeriesMetaAccessor(MetadataHandler):
|
|
57
|
+
"""
|
|
58
|
+
Series implementation of the Metadata Accessor.
|
|
59
|
+
Does not offer anything more than the base class at the moment.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, pandas_obj):
|
|
63
|
+
super().__init__(pandas_obj)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@register_dataframe_accessor("meta")
|
|
67
|
+
class DataFrameMetaAccessor(MetadataHandler):
|
|
68
|
+
"""
|
|
69
|
+
DataFrame implementation of the Metadata Accessor.
|
|
70
|
+
Introduces another attribute, '_header_level_detail', that stores the
|
|
71
|
+
currently displayed metadata as column names. Additionally offers methods
|
|
72
|
+
to sync metadata between the dataframe and its series, and the handling of
|
|
73
|
+
common operations, like renaming or the addition of new columns.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(self, pandas_obj):
|
|
77
|
+
super().__init__(pandas_obj)
|
|
78
|
+
if not hasattr(self._obj, "_header_level_detail"):
|
|
79
|
+
self._obj._header_level_detail = "shortname"
|
|
80
|
+
# Initialize DataFrame metadata
|
|
81
|
+
self.aggregate_series_metadata()
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def header_detail(self):
|
|
85
|
+
return self._obj._header_level_detail
|
|
86
|
+
|
|
87
|
+
@header_detail.setter
|
|
88
|
+
def header_detail(self, value):
|
|
89
|
+
self._obj._header_level_detail = value
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def metadata(self):
|
|
93
|
+
return self._obj._metadata_store
|
|
94
|
+
|
|
95
|
+
@metadata.setter
|
|
96
|
+
def metadata(self, value):
|
|
97
|
+
meta_dict = {
|
|
98
|
+
shortname: self.add_default_metadata(shortname, metainfo)
|
|
99
|
+
for shortname, metainfo in value.items()
|
|
100
|
+
}
|
|
101
|
+
self._obj._metadata_store = meta_dict
|
|
102
|
+
self.propagate_metadata_to_series()
|
|
103
|
+
|
|
104
|
+
def aggregate_series_metadata(self):
|
|
105
|
+
"""Aggregate metadata from Series within the DataFrame."""
|
|
106
|
+
for column in self._obj.columns:
|
|
107
|
+
if isinstance(self._obj[column], pd.Series) and hasattr(
|
|
108
|
+
self._obj[column], "meta"
|
|
109
|
+
):
|
|
110
|
+
self.metadata[column] = self._obj[column].meta.metadata
|
|
111
|
+
|
|
112
|
+
def propagate_metadata_to_series(self):
|
|
113
|
+
"""Propagate DataFrame-level metadata back to Series."""
|
|
114
|
+
for column in self._obj.columns:
|
|
115
|
+
if isinstance(self._obj[column], pd.Series) and hasattr(
|
|
116
|
+
self._obj[column], "meta"
|
|
117
|
+
):
|
|
118
|
+
for key, value in self.metadata.items():
|
|
119
|
+
if key == column:
|
|
120
|
+
try:
|
|
121
|
+
self._obj[column].meta.metadata = value
|
|
122
|
+
except TypeError:
|
|
123
|
+
logger.error(f"{column}: {value}")
|
|
124
|
+
|
|
125
|
+
def update_metadata_on_rename(self, rename_dict):
|
|
126
|
+
"""Update metadata when columns are renamed."""
|
|
127
|
+
new_metadata = {}
|
|
128
|
+
for old_name, new_name in rename_dict.items():
|
|
129
|
+
for key, value in self.metadata.items():
|
|
130
|
+
if key == old_name:
|
|
131
|
+
new_metadata[new_name] = value
|
|
132
|
+
self.metadata = new_metadata
|
|
133
|
+
self.propagate_metadata_to_series()
|
|
134
|
+
|
|
135
|
+
def rename(self, rename_key):
|
|
136
|
+
"""Rename the column names by using a metadata point."""
|
|
137
|
+
rename_dict = {
|
|
138
|
+
column: (
|
|
139
|
+
self._obj[column].meta.get(rename_key)
|
|
140
|
+
if rename_key in list(self._obj[column].meta.metadata.keys())
|
|
141
|
+
else column
|
|
142
|
+
)
|
|
143
|
+
for column in self._obj.columns
|
|
144
|
+
}
|
|
145
|
+
self._obj.rename(columns=rename_dict, inplace=True)
|
|
146
|
+
self.header_detail = rename_key
|
|
147
|
+
self.update_metadata_on_rename(rename_dict)
|
|
148
|
+
|
|
149
|
+
def add_column(
|
|
150
|
+
self,
|
|
151
|
+
name: str,
|
|
152
|
+
data: pd.Series | list,
|
|
153
|
+
location: int | None = None,
|
|
154
|
+
metadata: dict = {},
|
|
155
|
+
):
|
|
156
|
+
"""Add a column and use or generate metadata for it."""
|
|
157
|
+
location = len(self._obj.columns) if location is None else location
|
|
158
|
+
self._obj.insert(
|
|
159
|
+
loc=location,
|
|
160
|
+
column=name,
|
|
161
|
+
value=data,
|
|
162
|
+
allow_duplicates=False,
|
|
163
|
+
)
|
|
164
|
+
self.metadata[name] = self.add_default_metadata(name, metadata)
|
|
165
|
+
self.propagate_metadata_to_series()
|
|
166
|
+
|
|
167
|
+
def add_default_metadata(
|
|
168
|
+
self,
|
|
169
|
+
name: str,
|
|
170
|
+
metadata: dict = {},
|
|
171
|
+
list_of_keys: list = [
|
|
172
|
+
"shortname",
|
|
173
|
+
"longinfo",
|
|
174
|
+
"name",
|
|
175
|
+
"metainfo",
|
|
176
|
+
"unit",
|
|
177
|
+
],
|
|
178
|
+
) -> dict:
|
|
179
|
+
"""Fill up missing metadata points with a default value."""
|
|
180
|
+
default = {}
|
|
181
|
+
for key in list_of_keys:
|
|
182
|
+
if key not in list(metadata.keys()):
|
|
183
|
+
default[key] = name
|
|
184
|
+
return {**metadata, **default}
|