sarapy 1.3.0__tar.gz → 2.0.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 (37) hide show
  1. {sarapy-1.3.0/sarapy.egg-info → sarapy-2.0.0}/PKG-INFO +20 -1
  2. {sarapy-1.3.0 → sarapy-2.0.0}/README.md +19 -0
  3. {sarapy-1.3.0 → sarapy-2.0.0}/pyproject.toml +1 -1
  4. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/dataProcessing/OpsProcessor.py +68 -114
  5. sarapy-2.0.0/sarapy/dataProcessing/TLMSensorDataProcessor.py +90 -0
  6. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/dataProcessing/TimeSeriesProcessor.py +19 -1
  7. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/mlProcessors/FertilizerTransformer.py +16 -29
  8. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/mlProcessors/PlantinClassifier.py +3 -3
  9. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/mlProcessors/PlantinFMCreator.py +39 -59
  10. sarapy-2.0.0/sarapy/preprocessing/TransformInputData.py +140 -0
  11. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/preprocessing/TransformToOutputData.py +4 -17
  12. sarapy-2.0.0/sarapy/stats/__init__.py +1 -0
  13. sarapy-2.0.0/sarapy/stats/stats.py +258 -0
  14. sarapy-2.0.0/sarapy/utils/plotting.py +96 -0
  15. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/version.py +1 -1
  16. {sarapy-1.3.0 → sarapy-2.0.0/sarapy.egg-info}/PKG-INFO +20 -1
  17. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy.egg-info/SOURCES.txt +4 -1
  18. sarapy-1.3.0/sarapy/dataProcessing/TLMSensorDataProcessor.py +0 -781
  19. sarapy-1.3.0/sarapy/preprocessing/TransformInputData.py +0 -110
  20. {sarapy-1.3.0 → sarapy-2.0.0}/LICENCE +0 -0
  21. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/__init__.py +0 -0
  22. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/dataProcessing/GeoProcessor.py +0 -0
  23. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/dataProcessing/__init__.py +0 -0
  24. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/mlProcessors/FertilizerFMCreator.py +0 -0
  25. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/mlProcessors/__init__.py +0 -0
  26. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/preprocessing/DistancesImputer.py +0 -0
  27. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/preprocessing/FertilizerImputer.py +0 -0
  28. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/preprocessing/__init__.py +0 -0
  29. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/utils/__init__.py +0 -0
  30. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/utils/amg_decoder.py +0 -0
  31. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/utils/amg_ppk.py +0 -0
  32. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy/utils/getRawOperations.py +0 -0
  33. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy.egg-info/dependency_links.txt +0 -0
  34. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy.egg-info/requires.txt +0 -0
  35. {sarapy-1.3.0 → sarapy-2.0.0}/sarapy.egg-info/top_level.txt +0 -0
  36. {sarapy-1.3.0 → sarapy-2.0.0}/setup.cfg +0 -0
  37. {sarapy-1.3.0 → sarapy-2.0.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sarapy
3
- Version: 1.3.0
3
+ Version: 2.0.0
4
4
  Home-page: https://github.com/lucasbaldezzari/sarapy
5
5
  Author: Lucas Baldezzari
6
6
  Author-email: Lucas Baldezzari <lmbaldezzari@gmail.com>
@@ -19,6 +19,25 @@ Requires-Dist: geopy
19
19
 
20
20
  Library for processing SARAPICO project metadata of _AMG SA_.
21
21
 
22
+ #### Version 2.0.0
23
+
24
+ - Se realizan cambios en TransformInputData para que convierta los datos de telemetría a la forma de lista de diccionarios, como un JSON, para que sea más fácil de usar y entender. Además, a futuro, se espera que los datos de la electrónica se puedan enviar en este formato.
25
+ - Se realizan cambios en TLMSensorDataProcessor para que pueda recibir los datos de telemetría como una lista de diccionarios. La clase permite obtener los datos como un numpy array, o indexar por filas y columnas de forma similar a un DataFrame de pandas (de hecho se usa pandas para esto).
26
+ - Se generan cambios en PlantinFMCreator para que pueda recibir los datos de telemetría como una lista de diccionarios, y se usa TLMSensorDataProcessor para procesar los datos y obtener la matriz de características, la cual es la misma que antes.
27
+ - Se generan cambios en FertilizerTransformer para que pueda recibir los datos de telemetría como una lista de diccionarios, y se usa TLMSensorDataProcessor para procesar los datos y obtener la matriz de características, la cual es la misma que antes.
28
+ - Se generan cambios en OpsProcessor para poder trabajar con el formato nuevo de datos.
29
+
30
+ Los detalles de los cambios son comentados en el issue #37.
31
+
32
+ #### Version 1.4.0b1
33
+
34
+ - Se empieza a trabajar sobre librería estadística para poder detectar sensores saturados.
35
+ - Se empieza a trabajar sobre actualización para trabajar con los datos en la nueva modalidad luego de la migración.
36
+
37
+ #### Version 1.3.1
38
+
39
+ - Se modifica PlantinClassifier para tomar el umbral máximo de dst_pt en 4 y no en 7.
40
+
22
41
  #### Version 1.3.0
23
42
 
24
43
  - Se agrega funcionalidad para estimar la cantidad de gramos de fertilizante en base al nivel de distorsión de fertilizante arrojado por la electrónica. Se implementa mlProcessor.FertlizerFMCreator y mlProcessor.FertilizerTransformer. Además se modifican las clases OpsProcessor y TransformToOutputData para poder usar estas nuevas clases.
@@ -2,6 +2,25 @@
2
2
 
3
3
  Library for processing SARAPICO project metadata of _AMG SA_.
4
4
 
5
+ #### Version 2.0.0
6
+
7
+ - Se realizan cambios en TransformInputData para que convierta los datos de telemetría a la forma de lista de diccionarios, como un JSON, para que sea más fácil de usar y entender. Además, a futuro, se espera que los datos de la electrónica se puedan enviar en este formato.
8
+ - Se realizan cambios en TLMSensorDataProcessor para que pueda recibir los datos de telemetría como una lista de diccionarios. La clase permite obtener los datos como un numpy array, o indexar por filas y columnas de forma similar a un DataFrame de pandas (de hecho se usa pandas para esto).
9
+ - Se generan cambios en PlantinFMCreator para que pueda recibir los datos de telemetría como una lista de diccionarios, y se usa TLMSensorDataProcessor para procesar los datos y obtener la matriz de características, la cual es la misma que antes.
10
+ - Se generan cambios en FertilizerTransformer para que pueda recibir los datos de telemetría como una lista de diccionarios, y se usa TLMSensorDataProcessor para procesar los datos y obtener la matriz de características, la cual es la misma que antes.
11
+ - Se generan cambios en OpsProcessor para poder trabajar con el formato nuevo de datos.
12
+
13
+ Los detalles de los cambios son comentados en el issue #37.
14
+
15
+ #### Version 1.4.0b1
16
+
17
+ - Se empieza a trabajar sobre librería estadística para poder detectar sensores saturados.
18
+ - Se empieza a trabajar sobre actualización para trabajar con los datos en la nueva modalidad luego de la migración.
19
+
20
+ #### Version 1.3.1
21
+
22
+ - Se modifica PlantinClassifier para tomar el umbral máximo de dst_pt en 4 y no en 7.
23
+
5
24
  #### Version 1.3.0
6
25
 
7
26
  - Se agrega funcionalidad para estimar la cantidad de gramos de fertilizante en base al nivel de distorsión de fertilizante arrojado por la electrónica. Se implementa mlProcessor.FertlizerFMCreator y mlProcessor.FertilizerTransformer. Además se modifican las clases OpsProcessor y TransformToOutputData para poder usar estas nuevas clases.
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "sarapy"
3
3
 
4
- version = "1.3.0"
4
+ version = "2.0.0"
5
5
  authors = [
6
6
  {name = "Lucas Baldezzari", email = "lmbaldezzari@gmail.com"},]
7
7
  maintainers = [
@@ -5,6 +5,12 @@ from sarapy.mlProcessors import PlantinFMCreator
5
5
  from sarapy.mlProcessors import PlantinClassifier
6
6
  from sarapy.preprocessing import TransformInputData, TransformToOutputData
7
7
  from sarapy.mlProcessors import FertilizerFMCreator, FertilizerTransformer
8
+ import logging
9
+
10
+ ##nivel de logging en warning para evitar mensajes de advertencia de sklearn
11
+ logging.basicConfig(level=logging.WARNING,
12
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
13
+ datefmt='%Y-%m-%d %H:%M:%S')
8
14
 
9
15
  class OpsProcessor():
10
16
  """Clase para procesar las operaciones de los operarios. La información se toma de la base de datos
@@ -68,23 +74,7 @@ class OpsProcessor():
68
74
  Se retorna un array con las clasificaciones concatenadas, manteniendo el orden de las operaciones por operario.
69
75
 
70
76
  Args:
71
- data: Es una lista de diccionario. Cada diccionario tiene los siguientes keys.
72
-
73
- Ejemplo:
74
-
75
- {
76
- "id_db_h":1, #int
77
- "ID_NPDP":"XXAA123", #string
78
- "FR": 1, #int
79
- "TLM_NPDP": b'\xfc\x01\t\t\x00\x00\x00\x98', #bytes
80
- "date_oprc":datetime.datetime(2024, 2, 16, 21, 2, 2, tzinfo=tzutc()),#datetime
81
- "Latitud":-32.145564789, #float
82
- "Longitud":-55.145564789, #float
83
- "Precision": 1000,
84
- "id_db_dw": 1 #int
85
- }
86
-
87
- - kwargs: Diccionario con los argumentos necesarios para la clasificación. Se utiliza para pasar argumentos a los métodos de clasificación.
77
+ ¡¡¡¡¡¡¡DOCUMENTAR!!!!!!!!!!!!!!!
88
78
 
89
79
  Returns:
90
80
  Lista de diccionarios con las clasificaciones. Cada diccionario tiene la forma
@@ -93,21 +83,22 @@ class OpsProcessor():
93
83
 
94
84
  ##chqueo que newSample no esté vacío
95
85
  if len(data) != 0:
96
- newSample = self.transformInputData.fit_transform(data)
86
+ newSample = self.transformInputData.transform(data)
97
87
  #Si tenemos nuevas operaciones, actualizamos el diccionario de operaciones
98
88
  self.updateOperationsDict(newSample) #actualizamos diccionario interno de la clase
99
89
  pl_clas = self.classifyForPlantin(**kwargs) #clasificamos las operaciones para plantín
100
90
 
101
91
  #estimamos los gramos de fertilizante
102
- dst_ft = self._ftfmcreator.transform(newSample[:,2]).astype(int)
103
- ft_grams = self._fertilizer_transformer.transform(dst_ft.reshape(-1,1))
92
+ ft_grams = self._fertilizer_transformer.transform(newSample)
93
+ logging.debug(f"Fertilizer grams shape: {ft_grams.shape}")
104
94
  id_db_h_nums, id_db_dw_nums = self.getActualOperationsNumbers() #obtenemos los números de operaciones desde el diccionario de operaciones
105
- date_oprc = newSample[:,3]
95
+ logging.debug(f"ID_DB_H shape: {id_db_h_nums.shape}, ID_DB_DW shape: {id_db_dw_nums.shape}")
96
+ date_oprc = pd.DataFrame(newSample)["date_oprc"].values.reshape(-1, 1) ##extraigo las fechas de operación de la muestra
97
+
106
98
  return self.transformToOutputData.fit_transform(np.column_stack((id_db_h_nums,
107
99
  id_db_dw_nums,
108
100
  pl_clas,
109
- ft_grams,
110
- date_oprc)))
101
+ ft_grams)))
111
102
  else:
112
103
  self.resetAllNewSamplesValues()
113
104
  return None
@@ -116,33 +107,20 @@ class OpsProcessor():
116
107
  """Actualiza el diccionario de operaciones.
117
108
 
118
109
  Args:
119
- - newSample: lista con los datos (numpy.array de strings) de las operaciones.
120
-
121
- - 0: id_db_h
122
- - 1: ID_NPDP
123
- - 2: TLM_NPDP
124
- - 3: date_oprc
125
- - 4: latitud
126
- - 5: longitud
127
- - 6: Precision
128
- - 7: FR
129
- - 8: id_db_dw
130
-
131
- Returns:
132
- - None
133
- NOTA: PENSAR SI SE DEVUELVE ALGO COMO UN TRUE O FALSE PARA SABER SI SE ACTUALIZÓ O NO EL DICCIONARIO
134
- DE MANERA CORRECTA O HUBO ALGÚN PROBLEMA Y ASÍ VER QUÉ HACER EN EL MAIN
110
+ - newSample: lista de diccionarios con los datos de las operaciones.
111
+
135
112
  """
136
-
137
- ID_NPDPs_newOperations = np.unique(newSample[:,1]) ##identificadores de operarios con nuevas operaciones en la muestra
138
-
113
+ nodos_recibidos = np.array([row["ID_NPDP"] for row in newSample]) ##nodos recibidos en la muestra
114
+ ID_NPDPs_newOperations = np.unique(nodos_recibidos) ##identificadores de operarios con nuevas operaciones en la muestra
115
+ logging.debug(f"Received nodes: {ID_NPDPs_newOperations}")
116
+
139
117
  ##chqueo si estos ID_NPDPs ya están en el diccionario, sino los agrego
140
118
  for ID_NPDP in ID_NPDPs_newOperations:
141
119
  if ID_NPDP not in self._operationsDict:
142
120
  #El diccionario contiene la siguiente información:
143
- #sample_ops: np.array con las columnas de TLM_NPDP, date_oprc, lat, lon, precision
144
- #last_oprc: np.array de la última operación con las columnas de TLM_NPDP, date_oprc, lat, lon, precision
145
- #first_day_op_classified: booleano para saber si es la primera operación del día fue clasificada
121
+ #sample_ops: lista de diccionarios con los datos de las operaciones.
122
+ #last_oprc: diccionario con la última operación registrada.
123
+ #first_day_op_classified: booleano para saber si es la primera operación del día que fue clasificada
146
124
  self._operationsDict[ID_NPDP] = {"sample_ops": None,
147
125
  "last_oprc": None,
148
126
  "first_day_op_classified": False,
@@ -152,18 +130,18 @@ class OpsProcessor():
152
130
 
153
131
  ##actualizo el diccionario con las operaciones nuevas para aquellos operarios que correspondan
154
132
  for ID_NPDP in ID_NPDPs_newOperations:
155
- sample_ops = newSample[newSample[:,1] == ID_NPDP][:,2:] #me quedo con las columnas de TLM_NPDP, date_oprc, lat, lon, precision
156
- id_db_h = newSample[newSample[:,1] == ID_NPDP][:,0]
157
- id_db_dw = newSample[newSample[:,1] == ID_NPDP][:,8]
133
+ sample_ops = newSample
134
+ id_db_h = np.array([row["id_db_h"] for row in newSample]) ##extraigo los id_db_h de la muestra
135
+ id_db_dw = np.array([row["id_db_dw"] for row in newSample])
158
136
  ##actualizo el diccionario
159
137
  self._operationsDict[ID_NPDP]["sample_ops"] = sample_ops
160
- self._operationsDict[ID_NPDP]["id_db_h"] = id_db_h
161
- self._operationsDict[ID_NPDP]["id_db_dw"] = id_db_dw
138
+ self._operationsDict[ID_NPDP]["id_db_h"] = np.astype(id_db_h, str) ##convierto a int
139
+ self._operationsDict[ID_NPDP]["id_db_dw"] = np.astype(id_db_dw, str) ##convierto a int
162
140
  ##chequeo si tenemos última operación, si es así, asignamos dicha operación en la primera fila de sample_ops
163
141
  last_op = self._operationsDict[ID_NPDP]["last_oprc"]
164
142
  ###si last_op es not None y last_op no está vacía, entonces concatenamos last_op con sample_ops
165
- if last_op is not None and last_op.size != 0:
166
- self._operationsDict[ID_NPDP]["sample_ops"] = np.vstack((last_op, sample_ops))
143
+ if last_op is not None and len(last_op) != 0:
144
+ self._operationsDict[ID_NPDP]["sample_ops"] += last_op ##concatenamos la última operación con las nuevas operaciones
167
145
 
168
146
  self.updateNewSamplesValues(ID_NPDPs_newOperations) #actualizo el estado de 'new_sample' en el diccionario de operaciones
169
147
  self.updateLastOperations(ID_NPDPs_newOperations) #actualizo la última operación de una muestra de operaciones en el diccionario de operaciones
@@ -193,25 +171,27 @@ class OpsProcessor():
193
171
  plantinClassifications = None
194
172
 
195
173
  ##me quedo con los ID_NPDPs que tengan _operationsDict[ID_NPDP]["new_sample"] iguales a True
196
- ops_with_new_sample = [ID_NPDP for ID_NPDP in self.operationsDict.keys() if self.operationsDict[ID_NPDP]["new_sample"]]
174
+ ops_with_new_sample = [ID_NPDP for ID_NPDP in self._operationsDict.keys() if self.operationsDict[ID_NPDP]["new_sample"]]
197
175
 
198
176
  for ID_NPDP in ops_with_new_sample:#self.operationsDict.keys():
199
177
  ##clasificamos las operaciones para plantín
200
- operations = self.operationsDict[ID_NPDP]["sample_ops"]
178
+ operations = self._operationsDict[ID_NPDP]["sample_ops"]
179
+ logging.debug(f"Número de operaciones para el nodo {ID_NPDP}: {len(operations)}")
201
180
  features, dst_pt, inest_pt = self.plantinFMCreator.fit_transform(operations)
181
+ logging.debug(f"Features shape for {ID_NPDP}: {features.shape}")
202
182
  classified_ops = self._plantin_classifier.classify(features, dst_pt, inest_pt, **classify_kwargs)
183
+ logging.debug(f"Classified operations shape for {ID_NPDP}: {classified_ops.shape}")
203
184
 
204
185
  ##chequeo si first_day_op_classified es True, si es así, no se considera la primera fila de las classified_ops
205
- if self.operationsDict[ID_NPDP]["first_day_op_classified"]:
186
+ if self._operationsDict[ID_NPDP]["first_day_op_classified"]:
206
187
  classified_ops = classified_ops[1:]
207
188
 
208
189
  ##actualizo las operaciones que hayan sido hardcodeadas luego de despertar y/o reiniciar la electrónica
209
- classified_ops = self.updateBedoreAwake(classified_ops)
190
+ classified_ops = self.updateAfterAwake(classified_ops)
210
191
 
211
- # plantinClassifications = np.vstack((plantinClassifications, classified_ops)) if plantinClassifications is not None else classified_ops
212
192
  plantinClassifications = np.concatenate((plantinClassifications, classified_ops)) if plantinClassifications is not None else classified_ops
213
193
 
214
- self.operationsDict[ID_NPDP]["first_day_op_classified"] = True
194
+ self._operationsDict[ID_NPDP]["first_day_op_classified"] = True
215
195
 
216
196
  return plantinClassifications
217
197
 
@@ -219,18 +199,7 @@ class OpsProcessor():
219
199
  """Método para actualizar la última operación de una muestra de operaciones en el diccionario de operaciones
220
200
 
221
201
  Args:
222
- - newSample: lista con los datos (numpy.array de strings) de las operaciones.
223
- La forma de cada dato dentro de la lista newSample es (n,6). Las columnas de newSample son,
224
-
225
- - 0: id_db_h
226
- - 1: ID_NPDP
227
- - 2: TLM_NPDP
228
- - 3: date_oprc
229
- - 4: latitud
230
- - 5: longitud
231
- - 6: Precision
232
- - 7: FR
233
- - 8: id_db_dw
202
+ - newSample: lista de diccionarios con los datos de las operaciones.
234
203
  """
235
204
 
236
205
  for ID_NPDP in ID_NPDPs_newOperations:
@@ -245,8 +214,9 @@ class OpsProcessor():
245
214
 
246
215
  ##recorro el diccionario de operaciones y actualizo el estado de 'new_sample' a
247
216
  ##True para los ID_NPDPs que tienen nuevas operaciones y a False para los que no tienen nuevas operaciones
248
- for ID_NPDP in self.operationsDict.keys():
217
+ for ID_NPDP in self._operationsDict.keys():
249
218
  if ID_NPDP in ID_NPDPs_newOperations:
219
+ logging.debug(f"Actualizando 'new_sample' para nodo: {ID_NPDP}")
250
220
  self._operationsDict[ID_NPDP]["new_sample"] = True
251
221
  else:
252
222
  self._operationsDict[ID_NPDP]["new_sample"] = False
@@ -255,19 +225,20 @@ class OpsProcessor():
255
225
  """Método para resetar todos los valores de new_sample en el diccionario de operaciones.
256
226
  """
257
227
 
258
- for ID_NPDP in self.operationsDict.keys():
228
+ for ID_NPDP in self._operationsDict.keys():
259
229
  self._operationsDict[ID_NPDP]["new_sample"] = False
260
230
 
261
231
  def getActualOperationsNumbers(self):
262
232
  """Método para obtener los números de operaciones desde el diccionario de operaciones para aquellos operarios que
263
233
  tienen nuevas operaciones en la muestra."""
264
-
234
+
265
235
  id_db_h_list = np.array([])
266
236
  id_db_dw_list = np.array([])
267
- for ID_NPDP in self.operationsDict.keys():
268
- if self.operationsDict[ID_NPDP]["new_sample"]:
269
- id_db_h_list = np.append(id_db_h_list, self.operationsDict[ID_NPDP]["id_db_h"].flatten())
270
- id_db_dw_list = np.append(id_db_dw_list, self.operationsDict[ID_NPDP]["id_db_dw"].flatten())
237
+ for ID_NPDP in self._operationsDict.keys():
238
+ if self._operationsDict[ID_NPDP]["new_sample"]:
239
+ logging.debug(f"Obteniendo números de operaciones para el ID_NPDP: {ID_NPDP}")
240
+ id_db_h_list = np.append(id_db_h_list, self._operationsDict[ID_NPDP]["id_db_h"].flatten())
241
+ id_db_dw_list = np.append(id_db_dw_list, self._operationsDict[ID_NPDP]["id_db_dw"].flatten())
271
242
 
272
243
  return id_db_h_list.astype(int), id_db_dw_list.astype(int)
273
244
 
@@ -275,7 +246,7 @@ class OpsProcessor():
275
246
  """Método para actualizar el indicador de si es la primera operación del día para cada operario en el diccionario de operaciones.
276
247
  """
277
248
 
278
- for ID_NPDP in self.operationsDict.keys():
249
+ for ID_NPDP in self._operationsDict.keys():
279
250
  self._operationsDict[ID_NPDP]["first_day_op_classified"] = False
280
251
 
281
252
  def cleanSamplesOperations(self):
@@ -294,10 +265,10 @@ class OpsProcessor():
294
265
  - 6: Precision
295
266
  """
296
267
 
297
- for ID_NPDP in self.operationsDict.keys():
268
+ for ID_NPDP in self._operationsDict.keys():
298
269
  self._operationsDict[ID_NPDP]["sample_ops"] = None
299
270
 
300
- def updateBedoreAwake(self, classified_ops):
271
+ def updateAfterAwake(self, classified_ops):
301
272
  """
302
273
  Función para actualizar las operaciones que hayan sido hardcodeadas luego de despertar y/o reiniciar la electrónica.
303
274
 
@@ -310,8 +281,8 @@ class OpsProcessor():
310
281
  - classified_ops: np.array con las operaciones clasificadas.
311
282
  """
312
283
 
313
- ##me quedo con los índices donde MODEFlag es igual a 1
314
- mask = self.plantinFMCreator.tlmExtracted[:,self.plantinFMCreator.tlmdeDP["MODEFlag"]]==1
284
+ ##me quedo con los índices donde N_MODE es igual a 1
285
+ mask = self.plantinFMCreator.tlmDataProcessor["N_MODE",:]==1
315
286
  classified_ops[mask] = 0 ##hardcodeo las operaciones que hayan sido clasificadas como 1
316
287
  return classified_ops
317
288
 
@@ -321,37 +292,20 @@ class OpsProcessor():
321
292
 
322
293
 
323
294
  if __name__ == "__main__":
324
- #cargo archivo examples\volcado_17112023_NODE_processed.csv
325
295
  import pandas as pd
326
- import numpy as np
327
- import os
328
- import sarapy.utils.getRawOperations as getRawOperations
329
- from sarapy.dataProcessing import OpsProcessor
330
-
331
- data_path = os.path.join(os.getcwd(), "examples\\2024-09-16\\UPM001N\\data.json")
332
- historical_data_path = os.path.join(os.getcwd(), "examples\\2024-09-16\\UPM001N\\historical-data.json")
333
-
334
- raw_data = pd.read_json(data_path, orient="records").to_dict(orient="records")
335
- raw_data2 = pd.read_json(historical_data_path, orient="records").to_dict(orient="records")
336
-
337
- raw_ops = getRawOperations.getRawOperations(raw_data, raw_data2)
338
-
339
- import time
340
- start_time = time.time()
341
- op = OpsProcessor.OpsProcessor(classifier_file='modelos\\pipeline_rf.pkl', imputeDistances = False,
342
- regresor_file='modelos\\regresor.pkl', poly_features_file='modelos\\poly_features.pkl')
343
- classifications = op.processOperations(raw_ops, update_samePlace=True, useRatioStats=True)
344
- end_time = time.time()
345
- execution_time = end_time - start_time
346
- print("Execution time:", execution_time, "seconds")
296
+ import json
297
+ import logging
298
+
299
+
300
+ historical_data_path = "examples/2025-06-21/UPM000N/historical-data.json"
301
+ with open(historical_data_path, 'r') as file:
302
+ samples = json.load(file)
303
+
304
+ samples1 = samples[:100]
305
+ samples2 = samples[100:200]
306
+
307
+ op = OpsProcessor(classifier_file='modelos\\pipeline_rf.pkl', imputeDistances = False,
308
+ regresor_file='modelos\\regresor.pkl', poly_features_file='modelos\\poly_features.pkl')
347
309
 
348
- ##
349
- df = pd.DataFrame(classifications)
350
- tag_seedling = df["tag_seedling"].values
351
- print(tag_seedling.mean())
352
- print(df["tag_seedling"].shape)
353
-
354
- ##datos de fertilizante
355
- tag_fertilizer = df["tag_fertilizer"].values
356
- print(tag_fertilizer[1500:1560])
357
- print(tag_fertilizer.mean())
310
+ op.processOperations(samples[:2])
311
+ # op.processOperations(samples2)
@@ -0,0 +1,90 @@
1
+ ###Documentación en https://github.com/lucasbaldezzari/sarapy/blob/main/docs/Docs.md
2
+ import numpy as np
3
+ import pandas as pd
4
+
5
+ class TLMSensorDataProcessor():
6
+ """- Autor: BALDEZZARI Lucas
7
+
8
+ Estar clase sirve para entegar los datos de telemetría como un array de numpy. La idea es
9
+ poder acceder a los datos a través de indexación, ejemplos:
10
+ tlm_data[["INST_PT","INST_FT"],:] en este caso se obtienen los datos de las columnas INST_PT e INST_FT
11
+
12
+ """
13
+
14
+ def __init__(self, data:list):
15
+ """Constructor de la clase MetadataManager
16
+
17
+ Args:
18
+ - data: Es una lista de diccionarios (como un JSON) con los datos de telemetría.
19
+ """
20
+ #convierto a un DataFrame de pandas
21
+ self.data = pd.DataFrame(data)
22
+ self.keys = self.data.columns.tolist()
23
+
24
+ def getData(self):
25
+ """Devuelve los datos de telemetría como un numpy array.
26
+
27
+ Los datos se retornan como un array de numpy, donde cada fila es una operación y cada columna es un sensor.
28
+ Es decir, los datos son de la forma (n_operaciones, n_columnas).
29
+ """
30
+ return self.data.values
31
+
32
+ def __getitem__(self, key):
33
+ """
34
+ Permite indexar el objeto como un array o DataFrame.
35
+
36
+ Ejemplos:
37
+ obj[["col1", "col2"], :] -> columnas col1 y col2, todas las filas
38
+ obj[:, :100] -> todas las columnas, primeras 100 filas
39
+ obj[:] -> todo
40
+ obj[["col1"], :50] -> columna col1, primeras 50 filas
41
+ """
42
+ if isinstance(key, tuple): ##reviso si es una tupla
43
+ ##se supone que key es una tupla de la forma (cols, rows)
44
+ if len(key) != 2:
45
+ raise ValueError("La clave debe ser una tupla de la forma (cols, rows)")
46
+ cols, rows = key
47
+
48
+ # Columnas
49
+ if isinstance(cols, list) or isinstance(cols, str):
50
+ selected_cols = cols
51
+ elif cols == slice(None): # equivalente a ":"
52
+ selected_cols = self.data.columns
53
+ else:
54
+ raise TypeError(f"Tipo de indexado de columnas no soportado: {type(cols)}")
55
+
56
+ # Filas
57
+ if isinstance(rows, int) or isinstance(rows, slice):
58
+ #retornamos de la forma (n_operaciones, n_columnas)
59
+ return self.data.loc[rows, selected_cols].values
60
+ else:
61
+ raise TypeError(f"Tipo de indexado de filas no soportado: {type(rows)}")
62
+
63
+ elif isinstance(key, slice):
64
+ # Caso para todas las filas y columnas
65
+ # Retornamos de la forma (n_operaciones, n_columnas)
66
+ return self.data.iloc[key].values
67
+
68
+ else:
69
+ raise TypeError("La indexación debe ser una tupla (cols, rows) o un slice ':'")
70
+
71
+ if __name__ == "__main__":
72
+ import pandas as pd
73
+ import json
74
+ from sarapy.preprocessing import TransformInputData
75
+
76
+ historical_data_path = "examples/2025-06-21/UPM000N/historical-data.json"
77
+ with open(historical_data_path, 'r') as file:
78
+ historical_data = json.load(file)
79
+
80
+ inputData_transformer = TransformInputData.TransformInputData()
81
+ data = inputData_transformer.transform(historical_data)
82
+
83
+ tlm_processor = TLMSensorDataProcessor(data=data)
84
+ print(tlm_processor.data.head())
85
+ tlm_processor[:, :10].shape
86
+
87
+ tlm_processor[["id_db_dw", "id_db_h"], :5]#.shape
88
+ tlm_processor.keys
89
+ tlm_processor["longitud",:]
90
+
@@ -54,7 +54,14 @@ class TimeSeriesProcessor(BaseEstimator, TransformerMixin):
54
54
  def transform(self, X: np.array):
55
55
  """Genera un array con los tiempos de operación, caminata, pico abierto y ratio_dCdP.
56
56
  Args:
57
- - X es un array de strings de forma (n, 2) donde la primera columna es el tiempo y la segunda columna es el tiempo de pico abierto (en segundos).
57
+ - X es un array de strings de forma (n, 2) donde la primera columna es el tiempo
58
+ y la segunda columna es el tiempo de pico abierto (en segundos).
59
+
60
+ Returns:
61
+ - Un array de numpy de forma (n, 4) donde la primera columna es
62
+ el tiempo de operación, la segunda columna es el tiempo de caminata,
63
+ la tercera columna es el tiempo de pico abierto y la cuarta columna es
64
+ el ratio entre el tiempo de caminata y el tiempo de pico abierto.
58
65
  """
59
66
 
60
67
  if not self.is_fitted:
@@ -66,6 +73,17 @@ class TimeSeriesProcessor(BaseEstimator, TransformerMixin):
66
73
  self._ratio_dCdP.reshape(-1,1))).round(2)
67
74
 
68
75
  def fit_transform(self, X: np.array, y=None):
76
+ """Genera un array con los tiempos de operación, caminata, pico abierto y ratio_dCdP.
77
+ Args:
78
+ - X es un array de strings de forma (n, 2) donde la primera columna es el tiempo
79
+ y la segunda columna es el tiempo de pico abierto (en segundos).
80
+
81
+ Returns:
82
+ - Un array de numpy de forma (n, 4) donde la primera columna es
83
+ el tiempo de operación, la segunda columna es el tiempo de caminata,
84
+ la tercera columna es el tiempo de pico abierto y la cuarta columna es
85
+ el ratio entre el tiempo de caminata y el tiempo de pico abierto.
86
+ """
69
87
  self.fit(X)
70
88
  return self.transform(X)
71
89
 
@@ -1,4 +1,5 @@
1
1
  import pickle
2
+ from sarapy.dataProcessing import TLMSensorDataProcessor
2
3
 
3
4
  class FertilizerTransformer:
4
5
  """
@@ -31,51 +32,37 @@ class FertilizerTransformer:
31
32
  self.fertilizer_grams = None ##cuando no se ha transformado ningún dato, se inicializa en None
32
33
 
33
34
 
34
- def transform(self, X):
35
+ def transform(self, data):
35
36
  """Transforma los datos de distorsión de fertilizante a gramos.
36
37
 
37
38
  Params:
38
- - X: Es un array con los datos de distorsión de fertilizante. La forma de X es (n,1)
39
-
40
- Ejemplo: [12. 1. 12. 0. 0. 0. 0. 0. 0. 12.]
39
+ - data: Es una lista de diccionarios (como un JSON) con los datos de telemetría.
41
40
 
42
41
  Returns:
43
42
  - 0: Array con los valores de distorsión de fertilizante transformados a gramos.
44
43
  """
45
-
46
- X_poly = self._poly_features.fit_transform(X)
44
+ tlmDataProcessor = TLMSensorDataProcessor.TLMSensorDataProcessor(data)
45
+ X = tlmDataProcessor["SC_FT",:]
46
+ X_poly = self._poly_features.fit_transform(X.reshape(-1, 1))
47
47
  self.fertilizer_grams = self._regresor.predict(X_poly)
48
48
 
49
49
  ##retorno con shape (n,)
50
50
  return self.fertilizer_grams.reshape(-1,)
51
51
 
52
52
  if __name__ == "__main__":
53
- import os
54
53
  import pandas as pd
55
- import numpy as np
54
+ import json
56
55
  from sarapy.preprocessing import TransformInputData
57
- from sarapy.mlProcessors import PlantinFMCreator
58
- import sarapy.utils.getRawOperations as getRawOperations
59
- tindata = TransformInputData.TransformInputData()
60
-
61
- ##cargo los archivos examples\2024-09-04\UPM001N\data.json y examples\2024-09-04\UPM001N\historical-data.json
62
- data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM001N\\data.json")
63
- historical_data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM001N\\historical-data.json")
64
- raw_data = pd.read_json(data_path, orient="records").to_dict(orient="records")
65
- raw_data2 = pd.read_json(historical_data_path, orient="records").to_dict(orient="records")
66
-
67
- raw_ops = np.array(getRawOperations.getRawOperations(raw_data, raw_data2))
68
- X = tindata.fit_transform(raw_ops) #transforma los datos de operaciones a un array de numpy
69
-
70
- from sarapy.mlProcessors import FertilizerFMCreator
71
56
 
72
- ftfmcreator = FertilizerFMCreator.FertilizerFMCreator()
73
- dst_ft = ftfmcreator.transform(X[:,2])
74
- ##convierto a int dst_ft
75
- dst_ft = dst_ft.astype(int)
57
+ historical_data_path = "examples/2025-06-21/UPM000N/historical-data.json"
58
+ with open(historical_data_path, 'r') as file:
59
+ historical_data = json.load(file)
76
60
 
77
- from sarapy.mlProcessors import FertilizerTransformer
61
+ ##cargo en un diccionario sarapy\preprocessing\telemetriaDataPosition.json
62
+ data_positions = json.load(open("sarapy/preprocessing/telemetriaDataPosition.json", 'r'))
63
+ transform_input_data = TransformInputData.TransformInputData()
64
+ transformed_data = transform_input_data.transform(historical_data)
78
65
 
79
- fertransformer = FertilizerTransformer.FertilizerTransformer(regresor_file='modelos\\regresor.pkl', poly_features_file='modelos\\poly_features.pkl')
80
- gramos = fertransformer.transform(dst_ft.reshape(-1,1))
66
+ fertransformer = FertilizerTransformer(regresor_file='modelos\\regresor.pkl', poly_features_file='modelos\\poly_features.pkl')
67
+ gramos = fertransformer.transform(transformed_data)
81
68
  print(gramos[:10])
@@ -129,12 +129,12 @@ class PlantinClassifier(BaseEstimator, TransformerMixin):
129
129
  Función para actualizar las etiquetas de las operaciones que tengan distorsiones de plantín.
130
130
  """
131
131
  new_labels = labels.copy()
132
+
132
133
  ##filtro si dst_pt es menor a 7 y si inest_pt es 0
133
- new_labels[(dst_pt < 7) & (inest_pt == 0)] = 0
134
+ new_labels[(dst_pt < 4) & (inest_pt == 0)] = 0
134
135
 
135
- ##si inest_pt es y umbral_proba es menor a umbra_proba, entonces la operación es 0
136
+ ##si inest_pt 1 es y umbral_proba es menor a umbra_proba, entonces la operación es 0
136
137
  new_labels[(inest_pt == 1) & (self.probas[:,1] < umbral_proba)] = 0
137
- # new_labels[(inest_pt == 1) & (self.probas[:,1] >= umbral_proba)] = 1
138
138
 
139
139
  return new_labels
140
140