tsp 1.7.3__py3-none-any.whl → 1.7.7__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 tsp might be problematic. Click here for more details.
- tsp/__init__.py +11 -11
- tsp/__meta__.py +1 -1
- tsp/core.py +1035 -1010
- tsp/data/2023-01-06_755-test-Dataset_2031-Constant_Over_Interval-Hourly-Ground_Temperature-Thermistor_Automated.timeserie.csv +4 -4
- tsp/data/2023-01-06_755-test.metadata.txt +208 -208
- tsp/data/NTGS_example_csv.csv +6 -0
- tsp/data/NTGS_example_slash_dates.csv +6 -0
- tsp/data/example_geotop.csv +5240 -5240
- tsp/data/example_gtnp.csv +1298 -1298
- tsp/data/example_permos.csv +8 -0
- tsp/data/test_geotop_has_space.txt +5 -5
- tsp/dataloggers/AbstractReader.py +43 -43
- tsp/dataloggers/FG2.py +110 -110
- tsp/dataloggers/GP5W.py +114 -114
- tsp/dataloggers/Geoprecision.py +34 -34
- tsp/dataloggers/HOBO.py +914 -914
- tsp/dataloggers/RBRXL800.py +190 -190
- tsp/dataloggers/RBRXR420.py +308 -308
- tsp/dataloggers/__init__.py +15 -15
- tsp/dataloggers/logr.py +115 -115
- tsp/dataloggers/test_files/004448.DAT +2543 -2543
- tsp/dataloggers/test_files/004531.DAT +17106 -17106
- tsp/dataloggers/test_files/004531.HEX +3587 -3587
- tsp/dataloggers/test_files/004534.HEX +3587 -3587
- tsp/dataloggers/test_files/010252.dat +1731 -1731
- tsp/dataloggers/test_files/010252.hex +1739 -1739
- tsp/dataloggers/test_files/010274.hex +1291 -1291
- tsp/dataloggers/test_files/010278.hex +3544 -3544
- tsp/dataloggers/test_files/012064.dat +1286 -1286
- tsp/dataloggers/test_files/012064.hex +1294 -1294
- tsp/dataloggers/test_files/012081.hex +3532 -3532
- tsp/dataloggers/test_files/07B1592.DAT +1483 -1483
- tsp/dataloggers/test_files/07B1592.HEX +1806 -1806
- tsp/dataloggers/test_files/07B4450.DAT +2234 -2234
- tsp/dataloggers/test_files/07B4450.HEX +2559 -2559
- tsp/dataloggers/test_files/CSc_CR1000_1.dat +295 -0
- tsp/dataloggers/test_files/FG2_399.csv +9881 -9881
- tsp/dataloggers/test_files/GP5W.csv +1121 -1121
- tsp/dataloggers/test_files/GP5W_260.csv +1884 -1884
- tsp/dataloggers/test_files/GP5W_270.csv +2210 -2210
- tsp/dataloggers/test_files/H08-030-08_HOBOware.csv +998 -998
- tsp/dataloggers/test_files/RBR_01.dat +1046 -1046
- tsp/dataloggers/test_files/RBR_02.dat +2426 -2426
- tsp/dataloggers/test_files/RSTDT2055.csv +2152 -2152
- tsp/dataloggers/test_files/U23-001_HOBOware.csv +1001 -1001
- tsp/dataloggers/test_files/hobo-negative-2.txt +6396 -6396
- tsp/dataloggers/test_files/hobo-negative-3.txt +5593 -5593
- tsp/dataloggers/test_files/hobo-positive-number-1.txt +1000 -1000
- tsp/dataloggers/test_files/hobo-positive-number-2.csv +1003 -1003
- tsp/dataloggers/test_files/hobo-positive-number-3.csv +1133 -1133
- tsp/dataloggers/test_files/hobo-positive-number-4.csv +1209 -1209
- tsp/dataloggers/test_files/hobo2.csv +8702 -8702
- tsp/dataloggers/test_files/hobo_1_AB.csv +21732 -21732
- tsp/dataloggers/test_files/hobo_1_AB_Details.txt +133 -133
- tsp/dataloggers/test_files/hobo_1_AB_classic.csv +4373 -4373
- tsp/dataloggers/test_files/hobo_1_AB_defaults.csv +21732 -21732
- tsp/dataloggers/test_files/hobo_1_AB_minimal.txt +1358 -1358
- tsp/dataloggers/test_files/hobo_1_AB_var2.csv +3189 -3189
- tsp/dataloggers/test_files/hobo_1_AB_var3.csv +2458 -2458
- tsp/dataloggers/test_files/logR_ULogC16-32_1.csv +106 -106
- tsp/dataloggers/test_files/logR_ULogC16-32_2.csv +100 -100
- tsp/dataloggers/test_files/mon_3_Ta_2010-08-18_2013-02-08.txt +21724 -21724
- tsp/dataloggers/test_files/rbr_001.dat +1133 -1133
- tsp/dataloggers/test_files/rbr_001.hex +1139 -1139
- tsp/dataloggers/test_files/rbr_001_no_comment.dat +1132 -1132
- tsp/dataloggers/test_files/rbr_001_no_comment.hex +1138 -1138
- tsp/dataloggers/test_files/rbr_002.dat +1179 -1179
- tsp/dataloggers/test_files/rbr_002.hex +1185 -1185
- tsp/dataloggers/test_files/rbr_003.hex +1292 -1292
- tsp/dataloggers/test_files/rbr_003.xls +0 -0
- tsp/dataloggers/test_files/rbr_xl_001.DAT +1105 -1105
- tsp/dataloggers/test_files/rbr_xl_002.DAT +1126 -1126
- tsp/dataloggers/test_files/rbr_xl_003.DAT +4622 -4622
- tsp/dataloggers/test_files/rbr_xl_003.HEX +3587 -3587
- tsp/gtnp.py +148 -141
- tsp/labels.py +3 -3
- tsp/misc.py +90 -90
- tsp/physics.py +101 -101
- tsp/plots/static.py +374 -305
- tsp/readers.py +548 -536
- tsp/scratch.py +6 -0
- tsp/time.py +45 -45
- tsp/tspwarnings.py +15 -0
- tsp/utils.py +101 -101
- tsp/version.py +1 -1
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/LICENSE +674 -674
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/METADATA +9 -5
- tsp-1.7.7.dist-info/RECORD +95 -0
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/WHEEL +5 -5
- tsp-1.7.3.dist-info/RECORD +0 -89
- {tsp-1.7.3.dist-info → tsp-1.7.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
time,0,0.5,1,1.5,2.3,3.6,5.09,7.08,9.57,12.56,20.04
|
|
2
|
+
2002-11-18 00:00:00,NA,-0.38333,-0.18833,-0.27,-0.25167,-0.45667,-0.68167,-0.67333,-0.76333,-0.67667,-0.53167
|
|
3
|
+
2002-11-19 00:00:00,NA,-0.53667,-0.1925,-0.2575,-0.24667,-0.45667,-0.6825,-0.685,-0.75083,-0.68083,-0.5375
|
|
4
|
+
2002-11-20 00:00:00,NA,-0.61083,-0.21167,-0.26333,-0.24333,-0.46417,-0.67833,-0.66833,-0.7525,-0.6825,-0.54
|
|
5
|
+
2002-11-21 00:00:00,NA,-0.46333,-0.2,-0.26333,-0.245,-0.45917,-0.66917,-0.67333,-0.74833,-0.68167,-0.54083
|
|
6
|
+
2002-11-22 00:00:00,NA,-0.35583,-0.195,-0.2625,-0.2425,-0.46333,-0.66417,-0.67333,-0.75,-0.68417,-0.54
|
|
7
|
+
2002-11-23 00:00:00,NA,-0.33333,-0.19833,-0.265,-0.2425,-0.46333,-0.66,-0.66833,-0.75,-0.68667,-0.53917
|
|
8
|
+
2002-11-24 00:00:00,NA,-0.32333,-0.20083,-0.2675,-0.2425,-0.46667,-0.65833,-0.66167,-0.7475,-0.68417,-0.54
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Date12[DDMMYYYYhhmm],Simulation_Period,Run,IDpoint,100.000000
|
|
2
|
-
01/12/2003 00:00,0,1,32,-1.000000
|
|
3
|
-
01/12/2003 01:00,1,1,32,-1.624711
|
|
4
|
-
01/12/2003 02:00,1,1,32,-2.366138
|
|
5
|
-
01/12/2003 03:00,1,1,32,-3.010153
|
|
1
|
+
Date12[DDMMYYYYhhmm],Simulation_Period,Run,IDpoint,100.000000
|
|
2
|
+
01/12/2003 00:00,0,1,32,-1.000000
|
|
3
|
+
01/12/2003 01:00,1,1,32,-1.624711
|
|
4
|
+
01/12/2003 02:00,1,1,32,-2.366138
|
|
5
|
+
01/12/2003 03:00,1,1,32,-3.010153
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
from abc import ABCMeta, abstractmethod
|
|
2
|
-
import pandas as pd
|
|
3
|
-
|
|
4
|
-
class AbstractReader(object):
|
|
5
|
-
__metaclass__ = ABCMeta
|
|
6
|
-
|
|
7
|
-
DATA = pd.DataFrame()
|
|
8
|
-
META = dict()
|
|
9
|
-
|
|
10
|
-
def __init__(self, datefmt=None):
|
|
11
|
-
"""
|
|
12
|
-
The generic class used to read logger data. Subclasses must be defined depending on what kind of instrument the data comes from. However, each reader behaves the same way. They all have the `.read()` method that takes the file name as an argument. You can create a reader first and then call the `.read()` method, or just run everything on one line.
|
|
13
|
-
|
|
14
|
-
Attributes
|
|
15
|
-
----------
|
|
16
|
-
DATA : pd.DataFrame
|
|
17
|
-
Reader objects should store csv data in a pandas dataframe in the `DATA` attribute. Column titles should mostly be left unchanged with the exception of the datetime column which is renamed to `TIME`. TIME should always the first column in the dataframe.
|
|
18
|
-
META : dict
|
|
19
|
-
Where possible, any metadata that is found in the file is stored in a `META` attribute.
|
|
20
|
-
|
|
21
|
-
Notes
|
|
22
|
-
-----
|
|
23
|
-
Datalogger metadata will differ between instruments. However, there are many commonalities. The following is an alphabetical list of metadata that should use common terminology and formatting in the `META` dictionary for each datalogger type.
|
|
24
|
-
|
|
25
|
-
.. code-block:: python
|
|
26
|
-
|
|
27
|
-
latitude : (CF) latitude where data was collected
|
|
28
|
-
longitude : (CF) longitude where data was collected
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Abbrevations:
|
|
32
|
-
CF - Climate and Forecast Conventions
|
|
33
|
-
|
|
34
|
-
"""
|
|
35
|
-
if datefmt:
|
|
36
|
-
self.DATEFMT = datefmt
|
|
37
|
-
|
|
38
|
-
@abstractmethod
|
|
39
|
-
def read(self, file) -> "pd.DataFrame":
|
|
40
|
-
"""read data from a file"""
|
|
41
|
-
|
|
42
|
-
def get_data(self):
|
|
43
|
-
return self.DATA
|
|
1
|
+
from abc import ABCMeta, abstractmethod
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
class AbstractReader(object):
|
|
5
|
+
__metaclass__ = ABCMeta
|
|
6
|
+
|
|
7
|
+
DATA = pd.DataFrame()
|
|
8
|
+
META = dict()
|
|
9
|
+
|
|
10
|
+
def __init__(self, datefmt=None):
|
|
11
|
+
"""
|
|
12
|
+
The generic class used to read logger data. Subclasses must be defined depending on what kind of instrument the data comes from. However, each reader behaves the same way. They all have the `.read()` method that takes the file name as an argument. You can create a reader first and then call the `.read()` method, or just run everything on one line.
|
|
13
|
+
|
|
14
|
+
Attributes
|
|
15
|
+
----------
|
|
16
|
+
DATA : pd.DataFrame
|
|
17
|
+
Reader objects should store csv data in a pandas dataframe in the `DATA` attribute. Column titles should mostly be left unchanged with the exception of the datetime column which is renamed to `TIME`. TIME should always the first column in the dataframe.
|
|
18
|
+
META : dict
|
|
19
|
+
Where possible, any metadata that is found in the file is stored in a `META` attribute.
|
|
20
|
+
|
|
21
|
+
Notes
|
|
22
|
+
-----
|
|
23
|
+
Datalogger metadata will differ between instruments. However, there are many commonalities. The following is an alphabetical list of metadata that should use common terminology and formatting in the `META` dictionary for each datalogger type.
|
|
24
|
+
|
|
25
|
+
.. code-block:: python
|
|
26
|
+
|
|
27
|
+
latitude : (CF) latitude where data was collected
|
|
28
|
+
longitude : (CF) longitude where data was collected
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Abbrevations:
|
|
32
|
+
CF - Climate and Forecast Conventions
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
if datefmt:
|
|
36
|
+
self.DATEFMT = datefmt
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def read(self, file) -> "pd.DataFrame":
|
|
40
|
+
"""read data from a file"""
|
|
41
|
+
|
|
42
|
+
def get_data(self):
|
|
43
|
+
return self.DATA
|
tsp/dataloggers/FG2.py
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
import re
|
|
3
|
-
import warnings
|
|
4
|
-
|
|
5
|
-
from .AbstractReader import AbstractReader
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class FG2(AbstractReader):
|
|
9
|
-
DATEFMT = "%d.%m.%Y %H:%M:%S"
|
|
10
|
-
DELIMITER = ","
|
|
11
|
-
HK = re.compile("^HK")
|
|
12
|
-
|
|
13
|
-
def __init__(self):
|
|
14
|
-
"""File reader for FG2
|
|
15
|
-
|
|
16
|
-
FG2 is the newer software for GeoPrecision instruments
|
|
17
|
-
|
|
18
|
-
Examples
|
|
19
|
-
--------
|
|
20
|
-
.. code-block:: python
|
|
21
|
-
|
|
22
|
-
from teaspoon.dataloggers import FG2
|
|
23
|
-
from pkg_resources import resource_filename
|
|
24
|
-
fg2_file = resource_filename("teaspoon", "dataloggers/test_files/FG2_399.csv")
|
|
25
|
-
|
|
26
|
-
# Read an FG2 file
|
|
27
|
-
FG2().read(fg2_file)
|
|
28
|
-
"""
|
|
29
|
-
pass
|
|
30
|
-
|
|
31
|
-
def read(self, file: str) -> "pd.DataFrame":
|
|
32
|
-
"""Read a FG2 geoprecision file
|
|
33
|
-
|
|
34
|
-
Parameters
|
|
35
|
-
----------
|
|
36
|
-
file : str
|
|
37
|
-
Path to an FG2 Geoprecision datalogger export
|
|
38
|
-
|
|
39
|
-
Returns
|
|
40
|
-
-------
|
|
41
|
-
pandas.DataFrame
|
|
42
|
-
A dataframe containing the data
|
|
43
|
-
"""
|
|
44
|
-
self.META['raw'] = list()
|
|
45
|
-
|
|
46
|
-
data = list()
|
|
47
|
-
|
|
48
|
-
with open(file, "r") as f:
|
|
49
|
-
for line in f:
|
|
50
|
-
if self.__is_header(line):
|
|
51
|
-
delimiters = line.count(self.DELIMITER)
|
|
52
|
-
columns = line.strip().split(self.DELIMITER)
|
|
53
|
-
|
|
54
|
-
elif self._is_observation(line):
|
|
55
|
-
line = line.strip()
|
|
56
|
-
line += self.DELIMITER * (delimiters - line.count(self.DELIMITER))
|
|
57
|
-
data.append(line.split(self.DELIMITER))
|
|
58
|
-
|
|
59
|
-
else:
|
|
60
|
-
self.META['raw'].append(line)
|
|
61
|
-
|
|
62
|
-
self.DATA = pd.DataFrame(data, columns=columns)
|
|
63
|
-
|
|
64
|
-
for col in self.DATA.columns:
|
|
65
|
-
if col == "TIME":
|
|
66
|
-
continue
|
|
67
|
-
|
|
68
|
-
try:
|
|
69
|
-
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='raise')
|
|
70
|
-
except ValueError:
|
|
71
|
-
warnings.warn("Could not successfully convert all data to numeric. Some data may be missing")
|
|
72
|
-
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
73
|
-
|
|
74
|
-
self.DATA["TIME"] = pd.to_datetime(self.DATA["TIME"], format=self.DATEFMT)
|
|
75
|
-
self.DATA = self.DATA.drop(["NO"], axis=1)
|
|
76
|
-
self.DATA = self.drop_hk(self.DATA)
|
|
77
|
-
|
|
78
|
-
self.META.update(parse_fg2_meta_lines(self.META['raw']))
|
|
79
|
-
|
|
80
|
-
return self.DATA
|
|
81
|
-
|
|
82
|
-
def _is_metadata(self, line) -> bool:
|
|
83
|
-
match = re.search("^<.*>$", line)
|
|
84
|
-
return bool(match)
|
|
85
|
-
|
|
86
|
-
def _is_observation(self, line: str) -> bool:
|
|
87
|
-
match = re.search(fr"^\d*{self.DELIMITER}\d\d.\d\d", line)
|
|
88
|
-
return bool(match)
|
|
89
|
-
|
|
90
|
-
def __is_header(self, line: str) -> bool:
|
|
91
|
-
match = re.search(f"NO{self.DELIMITER}TIME", line)
|
|
92
|
-
return bool(match)
|
|
93
|
-
|
|
94
|
-
def _is_hk(self, name: str) -> bool:
|
|
95
|
-
if self.HK.match(name):
|
|
96
|
-
return True
|
|
97
|
-
return False
|
|
98
|
-
|
|
99
|
-
def drop_hk(self, df: "pd.DataFrame") -> "pd.DataFrame":
|
|
100
|
-
return df.drop([c for c in df if self._is_hk(c)], axis=1)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def parse_fg2_meta_lines(meta: "list[str]") -> dict:
|
|
104
|
-
parsed = dict()
|
|
105
|
-
serial = re.compile(r"LOGGER: \$([\w]{6})")
|
|
106
|
-
|
|
107
|
-
for line in meta:
|
|
108
|
-
if serial.match(line):
|
|
109
|
-
parsed["logger_serial_number"] = serial.match(line).group(1)
|
|
110
|
-
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import re
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
from .AbstractReader import AbstractReader
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FG2(AbstractReader):
|
|
9
|
+
DATEFMT = "%d.%m.%Y %H:%M:%S"
|
|
10
|
+
DELIMITER = ","
|
|
11
|
+
HK = re.compile("^HK")
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
"""File reader for FG2
|
|
15
|
+
|
|
16
|
+
FG2 is the newer software for GeoPrecision instruments
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
--------
|
|
20
|
+
.. code-block:: python
|
|
21
|
+
|
|
22
|
+
from teaspoon.dataloggers import FG2
|
|
23
|
+
from pkg_resources import resource_filename
|
|
24
|
+
fg2_file = resource_filename("teaspoon", "dataloggers/test_files/FG2_399.csv")
|
|
25
|
+
|
|
26
|
+
# Read an FG2 file
|
|
27
|
+
FG2().read(fg2_file)
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def read(self, file: str) -> "pd.DataFrame":
|
|
32
|
+
"""Read a FG2 geoprecision file
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
file : str
|
|
37
|
+
Path to an FG2 Geoprecision datalogger export
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
pandas.DataFrame
|
|
42
|
+
A dataframe containing the data
|
|
43
|
+
"""
|
|
44
|
+
self.META['raw'] = list()
|
|
45
|
+
|
|
46
|
+
data = list()
|
|
47
|
+
|
|
48
|
+
with open(file, "r") as f:
|
|
49
|
+
for line in f:
|
|
50
|
+
if self.__is_header(line):
|
|
51
|
+
delimiters = line.count(self.DELIMITER)
|
|
52
|
+
columns = line.strip().split(self.DELIMITER)
|
|
53
|
+
|
|
54
|
+
elif self._is_observation(line):
|
|
55
|
+
line = line.strip()
|
|
56
|
+
line += self.DELIMITER * (delimiters - line.count(self.DELIMITER))
|
|
57
|
+
data.append(line.split(self.DELIMITER))
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
self.META['raw'].append(line)
|
|
61
|
+
|
|
62
|
+
self.DATA = pd.DataFrame(data, columns=columns)
|
|
63
|
+
|
|
64
|
+
for col in self.DATA.columns:
|
|
65
|
+
if col == "TIME":
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='raise')
|
|
70
|
+
except ValueError:
|
|
71
|
+
warnings.warn("Could not successfully convert all data to numeric. Some data may be missing")
|
|
72
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
73
|
+
|
|
74
|
+
self.DATA["TIME"] = pd.to_datetime(self.DATA["TIME"], format=self.DATEFMT)
|
|
75
|
+
self.DATA = self.DATA.drop(["NO"], axis=1)
|
|
76
|
+
self.DATA = self.drop_hk(self.DATA)
|
|
77
|
+
|
|
78
|
+
self.META.update(parse_fg2_meta_lines(self.META['raw']))
|
|
79
|
+
|
|
80
|
+
return self.DATA
|
|
81
|
+
|
|
82
|
+
def _is_metadata(self, line) -> bool:
|
|
83
|
+
match = re.search("^<.*>$", line)
|
|
84
|
+
return bool(match)
|
|
85
|
+
|
|
86
|
+
def _is_observation(self, line: str) -> bool:
|
|
87
|
+
match = re.search(fr"^\d*{self.DELIMITER}\d\d.\d\d", line)
|
|
88
|
+
return bool(match)
|
|
89
|
+
|
|
90
|
+
def __is_header(self, line: str) -> bool:
|
|
91
|
+
match = re.search(f"NO{self.DELIMITER}TIME", line)
|
|
92
|
+
return bool(match)
|
|
93
|
+
|
|
94
|
+
def _is_hk(self, name: str) -> bool:
|
|
95
|
+
if self.HK.match(name):
|
|
96
|
+
return True
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
def drop_hk(self, df: "pd.DataFrame") -> "pd.DataFrame":
|
|
100
|
+
return df.drop([c for c in df if self._is_hk(c)], axis=1)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def parse_fg2_meta_lines(meta: "list[str]") -> dict:
|
|
104
|
+
parsed = dict()
|
|
105
|
+
serial = re.compile(r"LOGGER: \$([\w]{6})")
|
|
106
|
+
|
|
107
|
+
for line in meta:
|
|
108
|
+
if serial.match(line):
|
|
109
|
+
parsed["logger_serial_number"] = serial.match(line).group(1)
|
|
110
|
+
|
|
111
111
|
return parsed
|
tsp/dataloggers/GP5W.py
CHANGED
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
|
|
2
|
-
import pandas as pd
|
|
3
|
-
import re
|
|
4
|
-
import warnings
|
|
5
|
-
|
|
6
|
-
from .AbstractReader import AbstractReader
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class GP5W(AbstractReader):
|
|
10
|
-
DATEFMT = "%d.%m.%Y %H:%M:%S"
|
|
11
|
-
HK = re.compile("^#HK")
|
|
12
|
-
|
|
13
|
-
def __init__(self):
|
|
14
|
-
"""File reader for GP5W
|
|
15
|
-
|
|
16
|
-
GP5W is the older software for GeoPrecision instruments
|
|
17
|
-
|
|
18
|
-
Examples
|
|
19
|
-
--------
|
|
20
|
-
|
|
21
|
-
.. code-block:: python
|
|
22
|
-
|
|
23
|
-
from teaspoon.dataloggers import GP5W
|
|
24
|
-
from pkg_resources import resource_filename
|
|
25
|
-
gp5w_file = resource_filename("teaspoon", "dataloggers/test_files/GP5W_260.csv")
|
|
26
|
-
|
|
27
|
-
# To read a file, you might first create a reader object
|
|
28
|
-
reader = GP5W()
|
|
29
|
-
reader.read(gp5w_file)
|
|
30
|
-
|
|
31
|
-
# Or instead, read the data in one line
|
|
32
|
-
data = GP5W().read(gp5w_file)
|
|
33
|
-
"""
|
|
34
|
-
super().__init__()
|
|
35
|
-
|
|
36
|
-
def read(self, file: str) -> "pd.DataFrame":
|
|
37
|
-
"""Read a GP5W geoprecision file
|
|
38
|
-
|
|
39
|
-
Parameters
|
|
40
|
-
----------
|
|
41
|
-
file : str
|
|
42
|
-
Path to an GP5W Geoprecision datalogger export
|
|
43
|
-
|
|
44
|
-
Returns
|
|
45
|
-
-------
|
|
46
|
-
pandas.DataFrame
|
|
47
|
-
A dataframe containing the data
|
|
48
|
-
|
|
49
|
-
"""
|
|
50
|
-
self.META['raw'] = list()
|
|
51
|
-
|
|
52
|
-
data = list()
|
|
53
|
-
|
|
54
|
-
with open(file, "r") as f:
|
|
55
|
-
for line in f:
|
|
56
|
-
if self.__is_header(line):
|
|
57
|
-
delimiters = line.count(",")
|
|
58
|
-
columns = line.strip().split(",")
|
|
59
|
-
|
|
60
|
-
elif self._is_observation(line):
|
|
61
|
-
line = line.strip()
|
|
62
|
-
line += "," * (delimiters - line.count(","))
|
|
63
|
-
data.append(line.split(","))
|
|
64
|
-
|
|
65
|
-
else:
|
|
66
|
-
self.META['raw'].append(line)
|
|
67
|
-
|
|
68
|
-
self.DATA = pd.DataFrame(data, columns=columns)
|
|
69
|
-
|
|
70
|
-
for col in self.DATA.columns:
|
|
71
|
-
if col == "Time":
|
|
72
|
-
continue
|
|
73
|
-
|
|
74
|
-
try:
|
|
75
|
-
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='raise')
|
|
76
|
-
except ValueError:
|
|
77
|
-
warnings.warn("Could not successfully convert all data to numeric. Some data may be missing")
|
|
78
|
-
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
79
|
-
|
|
80
|
-
self.DATA["Time"] = pd.to_datetime(self.DATA["Time"], format=self.DATEFMT)
|
|
81
|
-
self.DATA.rename(columns={"Time":"TIME"}, inplace=True)
|
|
82
|
-
|
|
83
|
-
self.DATA = self.DATA.drop(["No"], axis=1)
|
|
84
|
-
self.DATA = self.drop_hk(self.DATA)
|
|
85
|
-
|
|
86
|
-
self.META.update(parse_gp5w_meta_lines(self.META['raw']))
|
|
87
|
-
|
|
88
|
-
return self.DATA
|
|
89
|
-
|
|
90
|
-
def _is_observation(self, line: str) -> bool:
|
|
91
|
-
match = re.search(r"\d*,\d{2}\.\d{2}\.\d{4}", line)
|
|
92
|
-
return bool(match)
|
|
93
|
-
|
|
94
|
-
def __is_header(self, line: str) -> bool:
|
|
95
|
-
match = re.search("No,Time", line)
|
|
96
|
-
return bool(match)
|
|
97
|
-
|
|
98
|
-
def _is_hk(self, name: str) -> bool:
|
|
99
|
-
if self.HK.match(name):
|
|
100
|
-
return True
|
|
101
|
-
return False
|
|
102
|
-
|
|
103
|
-
def drop_hk(self, df: "pd.DataFrame") -> "pd.DataFrame":
|
|
104
|
-
return df.drop([c for c in df if self._is_hk(c)], axis=1)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def parse_gp5w_meta_lines(meta: "list[str]") -> dict:
|
|
108
|
-
parsed = dict()
|
|
109
|
-
serial = re.compile(r"Logger: \#([\w]{6})")
|
|
110
|
-
|
|
111
|
-
for line in meta:
|
|
112
|
-
if serial.match(line):
|
|
113
|
-
parsed["logger_serial_number"] = serial.match(line).group(1)
|
|
114
|
-
|
|
1
|
+
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import re
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from .AbstractReader import AbstractReader
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GP5W(AbstractReader):
|
|
10
|
+
DATEFMT = "%d.%m.%Y %H:%M:%S"
|
|
11
|
+
HK = re.compile("^#HK")
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
"""File reader for GP5W
|
|
15
|
+
|
|
16
|
+
GP5W is the older software for GeoPrecision instruments
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
--------
|
|
20
|
+
|
|
21
|
+
.. code-block:: python
|
|
22
|
+
|
|
23
|
+
from teaspoon.dataloggers import GP5W
|
|
24
|
+
from pkg_resources import resource_filename
|
|
25
|
+
gp5w_file = resource_filename("teaspoon", "dataloggers/test_files/GP5W_260.csv")
|
|
26
|
+
|
|
27
|
+
# To read a file, you might first create a reader object
|
|
28
|
+
reader = GP5W()
|
|
29
|
+
reader.read(gp5w_file)
|
|
30
|
+
|
|
31
|
+
# Or instead, read the data in one line
|
|
32
|
+
data = GP5W().read(gp5w_file)
|
|
33
|
+
"""
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
def read(self, file: str) -> "pd.DataFrame":
|
|
37
|
+
"""Read a GP5W geoprecision file
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
file : str
|
|
42
|
+
Path to an GP5W Geoprecision datalogger export
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
pandas.DataFrame
|
|
47
|
+
A dataframe containing the data
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
self.META['raw'] = list()
|
|
51
|
+
|
|
52
|
+
data = list()
|
|
53
|
+
|
|
54
|
+
with open(file, "r") as f:
|
|
55
|
+
for line in f:
|
|
56
|
+
if self.__is_header(line):
|
|
57
|
+
delimiters = line.count(",")
|
|
58
|
+
columns = line.strip().split(",")
|
|
59
|
+
|
|
60
|
+
elif self._is_observation(line):
|
|
61
|
+
line = line.strip()
|
|
62
|
+
line += "," * (delimiters - line.count(","))
|
|
63
|
+
data.append(line.split(","))
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
self.META['raw'].append(line)
|
|
67
|
+
|
|
68
|
+
self.DATA = pd.DataFrame(data, columns=columns)
|
|
69
|
+
|
|
70
|
+
for col in self.DATA.columns:
|
|
71
|
+
if col == "Time":
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='raise')
|
|
76
|
+
except ValueError:
|
|
77
|
+
warnings.warn("Could not successfully convert all data to numeric. Some data may be missing")
|
|
78
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
79
|
+
|
|
80
|
+
self.DATA["Time"] = pd.to_datetime(self.DATA["Time"], format=self.DATEFMT)
|
|
81
|
+
self.DATA.rename(columns={"Time":"TIME"}, inplace=True)
|
|
82
|
+
|
|
83
|
+
self.DATA = self.DATA.drop(["No"], axis=1)
|
|
84
|
+
self.DATA = self.drop_hk(self.DATA)
|
|
85
|
+
|
|
86
|
+
self.META.update(parse_gp5w_meta_lines(self.META['raw']))
|
|
87
|
+
|
|
88
|
+
return self.DATA
|
|
89
|
+
|
|
90
|
+
def _is_observation(self, line: str) -> bool:
|
|
91
|
+
match = re.search(r"\d*,\d{2}\.\d{2}\.\d{4}", line)
|
|
92
|
+
return bool(match)
|
|
93
|
+
|
|
94
|
+
def __is_header(self, line: str) -> bool:
|
|
95
|
+
match = re.search("No,Time", line)
|
|
96
|
+
return bool(match)
|
|
97
|
+
|
|
98
|
+
def _is_hk(self, name: str) -> bool:
|
|
99
|
+
if self.HK.match(name):
|
|
100
|
+
return True
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def drop_hk(self, df: "pd.DataFrame") -> "pd.DataFrame":
|
|
104
|
+
return df.drop([c for c in df if self._is_hk(c)], axis=1)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def parse_gp5w_meta_lines(meta: "list[str]") -> dict:
|
|
108
|
+
parsed = dict()
|
|
109
|
+
serial = re.compile(r"Logger: \#([\w]{6})")
|
|
110
|
+
|
|
111
|
+
for line in meta:
|
|
112
|
+
if serial.match(line):
|
|
113
|
+
parsed["logger_serial_number"] = serial.match(line).group(1)
|
|
114
|
+
|
|
115
115
|
return parsed
|
tsp/dataloggers/Geoprecision.py
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
from typing import Optional, Type
|
|
2
|
-
|
|
3
|
-
from tsp.dataloggers.AbstractReader import AbstractReader
|
|
4
|
-
from tsp.dataloggers.FG2 import FG2
|
|
5
|
-
from tsp.dataloggers.GP5W import GP5W
|
|
6
|
-
|
|
7
|
-
import re
|
|
8
|
-
|
|
9
|
-
firmwares = {GP5W: re.compile("GP5W", re.IGNORECASE),
|
|
10
|
-
FG2: re.compile("FG2", re.IGNORECASE)}
|
|
11
|
-
|
|
12
|
-
def detect_geoprecision_type(file: str) -> "Optional[Type[AbstractReader]]":
|
|
13
|
-
""" Detect whether a geoprecision file uses from a 'GP5W' or 'FG2' firmware and return a
|
|
14
|
-
|
|
15
|
-
Parameters
|
|
16
|
-
----------
|
|
17
|
-
file : str
|
|
18
|
-
Path to a geoprecision file.
|
|
19
|
-
|
|
20
|
-
Returns
|
|
21
|
-
-------
|
|
22
|
-
Optional[Type[AbstractReader]]
|
|
23
|
-
An appropriate file reader. If the file corresponds to neither GP5W or FG2, None is returned.
|
|
24
|
-
"""
|
|
25
|
-
with open(file, 'r') as f:
|
|
26
|
-
|
|
27
|
-
header = f.readline()
|
|
28
|
-
|
|
29
|
-
for g, pattern in firmwares.items():
|
|
30
|
-
if pattern.search(header):
|
|
31
|
-
return g
|
|
32
|
-
|
|
33
|
-
return None
|
|
34
|
-
|
|
1
|
+
from typing import Optional, Type
|
|
2
|
+
|
|
3
|
+
from tsp.dataloggers.AbstractReader import AbstractReader
|
|
4
|
+
from tsp.dataloggers.FG2 import FG2
|
|
5
|
+
from tsp.dataloggers.GP5W import GP5W
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
firmwares = {GP5W: re.compile("GP5W", re.IGNORECASE),
|
|
10
|
+
FG2: re.compile("FG2", re.IGNORECASE)}
|
|
11
|
+
|
|
12
|
+
def detect_geoprecision_type(file: str) -> "Optional[Type[AbstractReader]]":
|
|
13
|
+
""" Detect whether a geoprecision file uses from a 'GP5W' or 'FG2' firmware and return a
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
file : str
|
|
18
|
+
Path to a geoprecision file.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
Optional[Type[AbstractReader]]
|
|
23
|
+
An appropriate file reader. If the file corresponds to neither GP5W or FG2, None is returned.
|
|
24
|
+
"""
|
|
25
|
+
with open(file, 'r') as f:
|
|
26
|
+
|
|
27
|
+
header = f.readline()
|
|
28
|
+
|
|
29
|
+
for g, pattern in firmwares.items():
|
|
30
|
+
if pattern.search(header):
|
|
31
|
+
return g
|
|
32
|
+
|
|
33
|
+
return None
|
|
34
|
+
|