sarapy 1.3.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sarapy/dataProcessing/OpsProcessor.py +68 -114
- sarapy/dataProcessing/TLMSensorDataProcessor.py +73 -764
- sarapy/dataProcessing/TimeSeriesProcessor.py +19 -1
- sarapy/mlProcessors/FertilizerTransformer.py +16 -29
- sarapy/mlProcessors/PlantinClassifier.py +3 -3
- sarapy/mlProcessors/PlantinFMCreator.py +39 -59
- sarapy/preprocessing/TransformInputData.py +115 -85
- sarapy/preprocessing/TransformToOutputData.py +4 -17
- sarapy/stats/__init__.py +1 -0
- sarapy/stats/stats.py +258 -0
- sarapy/utils/plotting.py +96 -0
- sarapy/version.py +1 -1
- {sarapy-1.3.0.dist-info → sarapy-2.0.0.dist-info}/METADATA +20 -1
- sarapy-2.0.0.dist-info/RECORD +29 -0
- sarapy-1.3.0.dist-info/RECORD +0 -26
- {sarapy-1.3.0.dist-info → sarapy-2.0.0.dist-info}/LICENCE +0 -0
- {sarapy-1.3.0.dist-info → sarapy-2.0.0.dist-info}/WHEEL +0 -0
- {sarapy-1.3.0.dist-info → sarapy-2.0.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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:
|
|
144
|
-
#last_oprc:
|
|
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
|
|
156
|
-
id_db_h =
|
|
157
|
-
id_db_dw =
|
|
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
|
|
166
|
-
self._operationsDict[ID_NPDP]["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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
268
|
-
if self.
|
|
269
|
-
|
|
270
|
-
|
|
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.
|
|
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.
|
|
268
|
+
for ID_NPDP in self._operationsDict.keys():
|
|
298
269
|
self._operationsDict[ID_NPDP]["sample_ops"] = None
|
|
299
270
|
|
|
300
|
-
def
|
|
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
|
|
314
|
-
mask = self.plantinFMCreator.
|
|
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
|
|
327
|
-
import
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
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)
|