tsp 1.7.1__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.

Files changed (91) hide show
  1. tsp/__init__.py +11 -11
  2. tsp/__meta__.py +1 -1
  3. tsp/core.py +1035 -1010
  4. tsp/data/2023-01-06_755-test-Dataset_2031-Constant_Over_Interval-Hourly-Ground_Temperature-Thermistor_Automated.timeserie.csv +4 -4
  5. tsp/data/2023-01-06_755-test.metadata.txt +208 -208
  6. tsp/data/NTGS_example_csv.csv +6 -0
  7. tsp/data/NTGS_example_slash_dates.csv +6 -0
  8. tsp/data/example_geotop.csv +5240 -5240
  9. tsp/data/example_gtnp.csv +1298 -1298
  10. tsp/data/example_permos.csv +8 -0
  11. tsp/data/test_geotop_has_space.txt +5 -0
  12. tsp/dataloggers/AbstractReader.py +43 -43
  13. tsp/dataloggers/FG2.py +110 -110
  14. tsp/dataloggers/GP5W.py +114 -114
  15. tsp/dataloggers/Geoprecision.py +34 -34
  16. tsp/dataloggers/HOBO.py +914 -914
  17. tsp/dataloggers/RBRXL800.py +190 -190
  18. tsp/dataloggers/RBRXR420.py +308 -307
  19. tsp/dataloggers/__init__.py +15 -15
  20. tsp/dataloggers/logr.py +115 -115
  21. tsp/dataloggers/test_files/004448.DAT +2543 -2543
  22. tsp/dataloggers/test_files/004531.DAT +17106 -17106
  23. tsp/dataloggers/test_files/004531.HEX +3587 -3587
  24. tsp/dataloggers/test_files/004534.HEX +3587 -3587
  25. tsp/dataloggers/test_files/010252.dat +1731 -1731
  26. tsp/dataloggers/test_files/010252.hex +1739 -1739
  27. tsp/dataloggers/test_files/010274.hex +1291 -1291
  28. tsp/dataloggers/test_files/010278.hex +3544 -3544
  29. tsp/dataloggers/test_files/012064.dat +1286 -1286
  30. tsp/dataloggers/test_files/012064.hex +1294 -1294
  31. tsp/dataloggers/test_files/012081.hex +3532 -3532
  32. tsp/dataloggers/test_files/07B1592.DAT +1483 -1483
  33. tsp/dataloggers/test_files/07B1592.HEX +1806 -1806
  34. tsp/dataloggers/test_files/07B4450.DAT +2234 -2234
  35. tsp/dataloggers/test_files/07B4450.HEX +2559 -2559
  36. tsp/dataloggers/test_files/CSc_CR1000_1.dat +295 -0
  37. tsp/dataloggers/test_files/FG2_399.csv +9881 -9881
  38. tsp/dataloggers/test_files/GP5W.csv +1121 -1121
  39. tsp/dataloggers/test_files/GP5W_260.csv +1884 -1884
  40. tsp/dataloggers/test_files/GP5W_270.csv +2210 -2210
  41. tsp/dataloggers/test_files/H08-030-08_HOBOware.csv +998 -998
  42. tsp/dataloggers/test_files/RBR_01.dat +1046 -1046
  43. tsp/dataloggers/test_files/RBR_02.dat +2426 -2426
  44. tsp/dataloggers/test_files/RSTDT2055.csv +2152 -2152
  45. tsp/dataloggers/test_files/U23-001_HOBOware.csv +1001 -1001
  46. tsp/dataloggers/test_files/hobo-negative-2.txt +6396 -6396
  47. tsp/dataloggers/test_files/hobo-negative-3.txt +5593 -5593
  48. tsp/dataloggers/test_files/hobo-positive-number-1.txt +1000 -1000
  49. tsp/dataloggers/test_files/hobo-positive-number-2.csv +1003 -1003
  50. tsp/dataloggers/test_files/hobo-positive-number-3.csv +1133 -1133
  51. tsp/dataloggers/test_files/hobo-positive-number-4.csv +1209 -1209
  52. tsp/dataloggers/test_files/hobo2.csv +8702 -8702
  53. tsp/dataloggers/test_files/hobo_1_AB.csv +21732 -21732
  54. tsp/dataloggers/test_files/hobo_1_AB_Details.txt +133 -133
  55. tsp/dataloggers/test_files/hobo_1_AB_classic.csv +4373 -4373
  56. tsp/dataloggers/test_files/hobo_1_AB_defaults.csv +21732 -21732
  57. tsp/dataloggers/test_files/hobo_1_AB_minimal.txt +1358 -1358
  58. tsp/dataloggers/test_files/hobo_1_AB_var2.csv +3189 -3189
  59. tsp/dataloggers/test_files/hobo_1_AB_var3.csv +2458 -2458
  60. tsp/dataloggers/test_files/logR_ULogC16-32_1.csv +106 -106
  61. tsp/dataloggers/test_files/logR_ULogC16-32_2.csv +100 -100
  62. tsp/dataloggers/test_files/mon_3_Ta_2010-08-18_2013-02-08.txt +21724 -21724
  63. tsp/dataloggers/test_files/rbr_001.dat +1133 -1133
  64. tsp/dataloggers/test_files/rbr_001.hex +1139 -1139
  65. tsp/dataloggers/test_files/rbr_001_no_comment.dat +1132 -1132
  66. tsp/dataloggers/test_files/rbr_001_no_comment.hex +1138 -1138
  67. tsp/dataloggers/test_files/rbr_002.dat +1179 -1179
  68. tsp/dataloggers/test_files/rbr_002.hex +1185 -1185
  69. tsp/dataloggers/test_files/rbr_003.hex +1292 -1292
  70. tsp/dataloggers/test_files/rbr_003.xls +0 -0
  71. tsp/dataloggers/test_files/rbr_xl_001.DAT +1105 -1105
  72. tsp/dataloggers/test_files/rbr_xl_002.DAT +1126 -1126
  73. tsp/dataloggers/test_files/rbr_xl_003.DAT +4622 -4622
  74. tsp/dataloggers/test_files/rbr_xl_003.HEX +3587 -3587
  75. tsp/gtnp.py +148 -141
  76. tsp/labels.py +3 -3
  77. tsp/misc.py +90 -90
  78. tsp/physics.py +101 -101
  79. tsp/plots/static.py +374 -305
  80. tsp/readers.py +548 -536
  81. tsp/scratch.py +6 -0
  82. tsp/time.py +45 -45
  83. tsp/tspwarnings.py +15 -0
  84. tsp/utils.py +101 -101
  85. tsp/version.py +1 -1
  86. {tsp-1.7.1.dist-info → tsp-1.7.7.dist-info}/LICENSE +674 -674
  87. {tsp-1.7.1.dist-info → tsp-1.7.7.dist-info}/METADATA +10 -6
  88. tsp-1.7.7.dist-info/RECORD +95 -0
  89. {tsp-1.7.1.dist-info → tsp-1.7.7.dist-info}/WHEEL +5 -5
  90. tsp-1.7.1.dist-info/RECORD +0 -88
  91. {tsp-1.7.1.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
@@ -0,0 +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,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
@@ -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
+