tsp 1.8.1__py3-none-any.whl → 1.10.2__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.
Files changed (127) hide show
  1. tsp/__init__.py +11 -11
  2. tsp/__meta__.py +1 -1
  3. tsp/concatenation.py +159 -153
  4. tsp/core.py +1306 -1162
  5. tsp/data/2023-01-06_755-test-Dataset_2031-Constant_Over_Interval-Hourly-Ground_Temperature-Thermistor_Automated.timeserie.csv +4 -4
  6. tsp/data/2023-01-06_755-test.metadata.txt +208 -208
  7. tsp/data/NTGS_example_csv.csv +6 -6
  8. tsp/data/NTGS_example_slash_dates.csv +6 -6
  9. tsp/data/NTGS_gtr_example_excel.xlsx +0 -0
  10. tsp/data/example_geotop.csv +5240 -5240
  11. tsp/data/example_gtnp.csv +1298 -1298
  12. tsp/data/example_permos.csv +7 -7
  13. tsp/data/ntgs-db-multi.txt +3872 -0
  14. tsp/data/ntgs-db-single.txt +2251 -0
  15. tsp/data/test_geotop_has_space.txt +5 -5
  16. tsp/data/tsp_format_long.csv +10 -0
  17. tsp/data/tsp_format_wide_1.csv +7 -0
  18. tsp/data/tsp_format_wide_2.csv +7 -0
  19. tsp/dataloggers/AbstractReader.py +43 -43
  20. tsp/dataloggers/FG2.py +110 -110
  21. tsp/dataloggers/GP5W.py +114 -114
  22. tsp/dataloggers/Geoprecision.py +34 -34
  23. tsp/dataloggers/HOBO.py +930 -914
  24. tsp/dataloggers/RBRXL800.py +190 -190
  25. tsp/dataloggers/RBRXR420.py +371 -308
  26. tsp/dataloggers/Vemco.py +84 -0
  27. tsp/dataloggers/__init__.py +15 -15
  28. tsp/dataloggers/logr.py +196 -115
  29. tsp/dataloggers/test_files/004448.DAT +2543 -2543
  30. tsp/dataloggers/test_files/004531.DAT +17106 -17106
  31. tsp/dataloggers/test_files/004531.HEX +3587 -3587
  32. tsp/dataloggers/test_files/004534.HEX +3587 -3587
  33. tsp/dataloggers/test_files/010252.dat +1731 -1731
  34. tsp/dataloggers/test_files/010252.hex +1739 -1739
  35. tsp/dataloggers/test_files/010274.hex +1291 -1291
  36. tsp/dataloggers/test_files/010278.hex +3544 -3544
  37. tsp/dataloggers/test_files/012064.dat +1286 -1286
  38. tsp/dataloggers/test_files/012064.hex +1294 -1294
  39. tsp/dataloggers/test_files/012064_modified_start.hex +1294 -0
  40. tsp/dataloggers/test_files/012081.hex +3532 -3532
  41. tsp/dataloggers/test_files/013138_recovery_stamp.hex +1123 -0
  42. tsp/dataloggers/test_files/014037-2007.hex +95 -0
  43. tsp/dataloggers/test_files/019360_20160918_1146_SlumpIslandTopofHill.hex +11253 -0
  44. tsp/dataloggers/test_files/019360_20160918_1146_SlumpIslandTopofHill.xls +0 -0
  45. tsp/dataloggers/test_files/07B1592.DAT +1483 -1483
  46. tsp/dataloggers/test_files/07B1592.HEX +1806 -1806
  47. tsp/dataloggers/test_files/07B4450.DAT +2234 -2234
  48. tsp/dataloggers/test_files/07B4450.HEX +2559 -2559
  49. tsp/dataloggers/test_files/2022018_2025-09-18T22-16-16.txt +36 -0
  50. tsp/dataloggers/test_files/2022018_2025-09-18T22-16-16_raw.csv +2074 -0
  51. tsp/dataloggers/test_files/2022018_2025-09-18T22-16-16_temp.csv +2074 -0
  52. tsp/dataloggers/test_files/2025004_2025-12-02T17-07-28_cfg.txt +30 -0
  53. tsp/dataloggers/test_files/2025004_2025-12-02T17-07-28_raw.csv +35 -0
  54. tsp/dataloggers/test_files/2025004_2025-12-02T17-07-28_temp.csv +35 -0
  55. tsp/dataloggers/test_files/204087.xlsx +0 -0
  56. tsp/dataloggers/test_files/Asc-1455As02.000 +2982 -0
  57. tsp/dataloggers/test_files/Asc-1456As02.000 +2992 -0
  58. tsp/dataloggers/test_files/Asc-1457As02.000 +2917 -0
  59. tsp/dataloggers/test_files/BGC_BH15_019362_20140610_1253.hex +1729 -0
  60. tsp/dataloggers/test_files/Bin2944.csv +759 -0
  61. tsp/dataloggers/test_files/Bin5494.csv +2972 -0
  62. tsp/dataloggers/test_files/Bin6786.csv +272 -0
  63. tsp/dataloggers/test_files/FG2_399.csv +9881 -9881
  64. tsp/dataloggers/test_files/GP5W.csv +1121 -1121
  65. tsp/dataloggers/test_files/GP5W_260.csv +1884 -1884
  66. tsp/dataloggers/test_files/GP5W_270.csv +2210 -2210
  67. tsp/dataloggers/test_files/H08-030-08_HOBOware.csv +998 -998
  68. tsp/dataloggers/test_files/Minilog-II-T_350763_20190711_1.csv +2075 -0
  69. tsp/dataloggers/test_files/Minilog-II-T_350769_20190921_1.csv +6384 -0
  70. tsp/dataloggers/test_files/Minilog-II-T_354284_20190921_1.csv +4712 -0
  71. tsp/dataloggers/test_files/Minilog-T_7943_20140920_1.csv +5826 -0
  72. tsp/dataloggers/test_files/Minilog-T_8979_20140806_1.csv +2954 -0
  73. tsp/dataloggers/test_files/Minilog-T_975_20110824_1.csv +4343 -0
  74. tsp/dataloggers/test_files/RBR_01.dat +1046 -1046
  75. tsp/dataloggers/test_files/RBR_02.dat +2426 -2426
  76. tsp/dataloggers/test_files/RI03b_062831_20240905_1801.rsk +0 -0
  77. tsp/dataloggers/test_files/RI03b_062831_20240905_1801.xlsx +0 -0
  78. tsp/dataloggers/test_files/RSTDT2055.csv +2152 -2152
  79. tsp/dataloggers/test_files/U23-001_HOBOware.csv +1001 -1001
  80. tsp/dataloggers/test_files/hobo-negative-2.txt +6396 -6396
  81. tsp/dataloggers/test_files/hobo-negative-3.txt +5593 -5593
  82. tsp/dataloggers/test_files/hobo-positive-number-1.txt +1000 -1000
  83. tsp/dataloggers/test_files/hobo-positive-number-2.csv +1003 -1003
  84. tsp/dataloggers/test_files/hobo-positive-number-3.csv +1133 -1133
  85. tsp/dataloggers/test_files/hobo-positive-number-4.csv +1209 -1209
  86. tsp/dataloggers/test_files/hobo2.csv +8702 -8702
  87. tsp/dataloggers/test_files/hobo_1_AB.csv +21732 -21732
  88. tsp/dataloggers/test_files/hobo_1_AB_Details.txt +133 -133
  89. tsp/dataloggers/test_files/hobo_1_AB_classic.csv +4373 -4373
  90. tsp/dataloggers/test_files/hobo_1_AB_defaults.csv +21732 -21732
  91. tsp/dataloggers/test_files/hobo_1_AB_minimal.txt +1358 -1358
  92. tsp/dataloggers/test_files/hobo_1_AB_var2.csv +3189 -3189
  93. tsp/dataloggers/test_files/hobo_1_AB_var3.csv +2458 -2458
  94. tsp/dataloggers/test_files/logR_ULogC16-32_1.csv +106 -106
  95. tsp/dataloggers/test_files/logR_ULogC16-32_2.csv +100 -100
  96. tsp/dataloggers/test_files/mon_3_Ta_2010-08-18_2013-02-08.txt +21724 -21724
  97. tsp/dataloggers/test_files/rbr_001.dat +1133 -1133
  98. tsp/dataloggers/test_files/rbr_001.hex +1139 -1139
  99. tsp/dataloggers/test_files/rbr_001_no_comment.dat +1132 -1132
  100. tsp/dataloggers/test_files/rbr_001_no_comment.hex +1138 -1138
  101. tsp/dataloggers/test_files/rbr_002.dat +1179 -1179
  102. tsp/dataloggers/test_files/rbr_002.hex +1185 -1185
  103. tsp/dataloggers/test_files/rbr_003.hex +1292 -1292
  104. tsp/dataloggers/test_files/rbr_xl_001.DAT +1105 -1105
  105. tsp/dataloggers/test_files/rbr_xl_002.DAT +1126 -1126
  106. tsp/dataloggers/test_files/rbr_xl_003.DAT +4622 -4622
  107. tsp/dataloggers/test_files/rbr_xl_003.HEX +3587 -3587
  108. tsp/gtnp.py +148 -148
  109. tsp/labels.py +3 -3
  110. tsp/misc.py +90 -90
  111. tsp/physics.py +101 -101
  112. tsp/plots/static.py +388 -374
  113. tsp/readers.py +829 -548
  114. tsp/standardization/__init__.py +0 -0
  115. tsp/standardization/metadata.py +95 -0
  116. tsp/standardization/metadata_ref.py +0 -0
  117. tsp/standardization/validator.py +535 -0
  118. tsp/time.py +45 -45
  119. tsp/tspwarnings.py +27 -15
  120. tsp/utils.py +131 -101
  121. tsp/version.py +1 -1
  122. {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/METADATA +95 -86
  123. tsp-1.10.2.dist-info/RECORD +132 -0
  124. {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/licenses/LICENSE +674 -674
  125. {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/top_level.txt +1 -0
  126. tsp-1.8.1.dist-info/RECORD +0 -94
  127. {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/WHEEL +0 -0
@@ -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
@@ -0,0 +1,10 @@
1
+ #tsp_version:1.0
2
+ #key=value
3
+ #
4
+ timestamp,depth,temperature,quality_flag
5
+ 2025-01-01T13:00:00-06:00,1,4.2,
6
+ 2025-01-01T13:00:00-06:00,2,-0.1,
7
+ 2025-01-01T13:00:00-06:00,4,-2.3,4
8
+ 2025-01-02T13:00:00-06:00,1,1.5,
9
+ 2025-01-02T13:00:00-06:00,2,,1
10
+ 2025-01-02T13:00:00-06:00,4,-2.31,
@@ -0,0 +1,7 @@
1
+ #tsp_version=1.0
2
+ #
3
+ #
4
+ timestamp,0.1,3.5,4.25
5
+ 2025-01-01T13:00:00Z,1.2,-1.6,-2.4
6
+ 2025-01-01T14:00:00Z,,-1.6,-2.4
7
+ 2025-01-01T15:00:00Z,1.6,-1.6,-2.4
@@ -0,0 +1,7 @@
1
+ #tsp_version=1.0
2
+ #latitude=66
3
+ #longitude=71
4
+ timestamp,0.1,3.5,4.25
5
+ 2025-01-01T13:00:00-06:00,1.2,-1.6,-2.4
6
+ 2025-01-01T14:00:00-06:00,,-1.6,-2.4
7
+ 2025-01-01T15:00:00-06:00,1.6,-1.6,-2.4
@@ -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
+