sarapy 1.0.0__py3-none-any.whl → 1.1.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.
@@ -4,7 +4,7 @@ import datetime
4
4
  from dateutil.tz import tzutc
5
5
  import numpy as np
6
6
  import pandas as pd
7
- # from sarapy.mlProcessors import PlantinFMCreator
7
+ from sarapy.mlProcessors import PlantinFMCreator
8
8
  from sarapy.mlProcessors import PlantinClassifier
9
9
  from sarapy.preprocessing import TransformInputData, TransformToOutputData
10
10
 
@@ -22,20 +22,27 @@ class OpsProcessor():
22
22
  """Constructor de la clase OpsProcessor.
23
23
 
24
24
  Args:
25
- - distanciaMedia: Distancia media entre operaciones.
25
+ - kwargs: Diccionario con los argumentos necesarios instanciar algunas clases.
26
26
  """
27
27
 
28
- plclass_map = {"classifier_file","imputeDistances", "distanciaMedia",
29
- "umbral_precision"," dist_mismo_lugar", "max_dist",
30
- "umbral_ratio_dCdP", "deltaO_medio"}
28
+ plclass_map = {"classifier_file"}
31
29
 
32
30
  kwargs_plclass = {}
33
31
  ##recorro kwargs y usando plclass_map creo un nuevo diccionario con los valores que se pasaron
34
32
  for key, value in kwargs.items():
35
33
  if key in plclass_map:
36
34
  kwargs_plclass[key] = value
35
+
36
+ fmcreator_map = {"imputeDistances", "distanciaMedia", "umbral_precision",
37
+ "dist_mismo_lugar", "max_dist", "umbral_ratio_dCdP", "deltaO_medio"}
38
+ fmcreator_kargs = {}
39
+ ##recorro kwargs y usando fmcreator_map creo un nuevo diccionario con los valores que se pasaron
40
+ for key, value in kwargs.items():
41
+ if key in fmcreator_map:
42
+ fmcreator_kargs[key] = value
37
43
 
38
44
  self._plantin_classifier = PlantinClassifier.PlantinClassifier(**kwargs_plclass)
45
+ self.plantinFMCreator = PlantinFMCreator.PlantinFMCreator(**fmcreator_kargs)
39
46
  # self._fertilizerFMCreator = FertilizerFMCreator() ## PARA IMPLEMENTAR
40
47
 
41
48
  self._operationsDict = {} ##diccionario de operarios con sus operaciones
@@ -45,7 +52,7 @@ class OpsProcessor():
45
52
  self.transformInputData = TransformInputData.TransformInputData()
46
53
  self.transformToOutputData = TransformToOutputData.TransformToOutputData()
47
54
 
48
- def processOperations(self, data):
55
+ def processOperations(self, data, **kwargs):
49
56
  """Método para procesar las operaciones de los operarios.
50
57
 
51
58
  Se toma una nueva muestra y se procesa la información para clasificar las operaciones considerando el
@@ -68,6 +75,8 @@ class OpsProcessor():
68
75
  "Precision": 1000,
69
76
  "id_db_dw": 1 #int
70
77
  }
78
+
79
+ - kwargs: Diccionario con los argumentos necesarios para la clasificación. Se utiliza para pasar argumentos a los métodos de clasificación.
71
80
 
72
81
  Returns:
73
82
  Lista de diccionarios con las clasificaciones. Cada diccionario tiene la forma
@@ -79,7 +88,7 @@ class OpsProcessor():
79
88
  newSample = self.transformInputData.fit_transform(data)
80
89
  #Si tenemos nuevas operaciones, actualizamos el diccionario de operaciones
81
90
  self.updateOperationsDict(newSample) #actualizamos diccionario interno de la clase
82
- pl_clas = self.classifyForPlantin() #clasificamos las operaciones para plantín
91
+ pl_clas = self.classifyForPlantin(**kwargs) #clasificamos las operaciones para plantín
83
92
  ft_clas = newSample[:,7].astype(int) #clasificamos las operaciones para fertilizante
84
93
  id_db_h_nums, id_db_dw_nums = self.getActualOperationsNumbers() #obtenemos los números de operaciones desde el diccionario de operaciones
85
94
  date_oprc = newSample[:,3]
@@ -148,14 +157,27 @@ class OpsProcessor():
148
157
  self.updateNewSamplesValues(ID_NPDPs_newOperations) #actualizo el estado de 'new_sample' en el diccionario de operaciones
149
158
  self.updateLastOperations(ID_NPDPs_newOperations) #actualizo la última operación de una muestra de operaciones en el diccionario de operaciones
150
159
 
151
- def classifyForPlantin(self):
160
+ def classifyForPlantin(self, **kwargs):
152
161
  """Método para clasificar las operaciones para plantín.
153
162
  Se recorre el diccionario de operaciones y se clasifican las operaciones para plantín.
154
163
 
164
+ Args:
165
+ - kwargs: Diccionario con los argumentos necesarios para la clasificación. Se utiliza para pasar argumentos a los métodos de clasificación.
166
+
155
167
  Returns:
156
168
  - plantinClassifications: np.array con las clasificaciones de las operaciones para plantín.
157
169
  """
158
170
 
171
+ key_classify_map = {"feature_matrix", "update_samePlace",
172
+ "useRatioStats", "std_weight", "useDistancesStats",
173
+ "ratio_dcdp_umbral", "dist_umbral"}
174
+
175
+ ##recorro kwargs y usando key_classify_map creo un nuevo diccionario con los valores que se pasaron
176
+ classify_kwargs = {}
177
+ for key, value in kwargs.items():
178
+ if key in key_classify_map:
179
+ classify_kwargs[key] = value
180
+
159
181
  ##creamos/reiniciamos el array con las clasificaciones de las operaciones para plantín
160
182
  plantinClassifications = None
161
183
 
@@ -165,7 +187,8 @@ class OpsProcessor():
165
187
  for ID_NPDP in ops_with_new_sample:#self.operationsDict.keys():
166
188
  ##clasificamos las operaciones para plantín
167
189
  operations = self.operationsDict[ID_NPDP]["sample_ops"]
168
- classified_ops = self._plantin_classifier.classify(operations)
190
+ features, dst_pt, inest_pt = self.plantinFMCreator.fit_transform(operations)
191
+ classified_ops = self._plantin_classifier.classify(features, **classify_kwargs)
169
192
 
170
193
  ##chequeo si first_day_op_classified es True, si es así, no se considera la primera fila de las classified_ops
171
194
  if self.operationsDict[ID_NPDP]["first_day_op_classified"]:
@@ -270,17 +293,26 @@ if __name__ == "__main__":
270
293
  import pandas as pd
271
294
  import numpy as np
272
295
  import os
273
- from sarapy.utils.getRawOperations import getRawOperations
296
+ import sarapy.utils.getRawOperations as getRawOperations
297
+ from sarapy.dataProcessing import OpsProcessor
298
+
299
+ data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM012N\\data.json")
300
+ historical_data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM012N\\historical-data.json")
301
+
302
+ raw_data = pd.read_json(data_path, orient="records").to_dict(orient="records")
303
+ raw_data2 = pd.read_json(historical_data_path, orient="records").to_dict(orient="records")
274
304
 
275
- data_df_raw = getRawOperations("examples\\2024-05-30\\UPM007N\\data.json", "examples\\2024-05-30\\UPM007N\\historical-data.json")
305
+ raw_ops = getRawOperations.getRawOperations(raw_data, raw_data2)
276
306
 
277
307
  import time
278
308
  start_time = time.time()
279
- op = OpsProcessor(classifier_file="examples\\pip_lda_imp.pkl", imputeDistances = False)
280
- classifcations = op.processOperations(data_df_raw)
309
+ op = OpsProcessor.OpsProcessor(classifier_file='modelos\\pipeline_rf.pkl', imputeDistances = False)
310
+ classifications = op.processOperations(raw_ops, update_samePlace=True, useRatioStats=True)
281
311
  end_time = time.time()
282
312
  execution_time = end_time - start_time
283
313
  print("Execution time:", execution_time, "seconds")
284
314
 
285
- print(len(classifcations))
286
- classifcations[:10]
315
+ ##
316
+ df = pd.DataFrame(classifications)
317
+ tag_seedling = df["tag_seedling"].values
318
+ print(tag_seedling.mean())
@@ -8,64 +8,138 @@ import pickle
8
8
  class PlantinClassifier(BaseEstimator, TransformerMixin):
9
9
  """Clase para implementar el pipeline de procesamiento de datos para la clasificación del tipo de operación para plantines."""
10
10
 
11
- def __init__(self, classifier_file = "", **kwargs):
11
+ def __init__(self, classifier_file = ""):
12
12
  """Constructor de la clase PlantinClassifier.
13
13
 
14
14
  Args:
15
15
  - classifier_file: String con el nombre del archivo que contiene el clasificador entrenado. El archivo a cargar es un archivo .pkl.
16
16
  """
17
-
18
- plclass_map = {"imputeDistances", "distanciaMedia", "umbral_precision"," dist_mismo_lugar", "max_dist",
19
- "umbral_ratio_dCdP", "deltaO_medio"}
20
-
21
- kwargs_plfmc = {}
22
-
23
- ##recorro kwargs y usando plclass_map creo un nuevo diccionario con los valores que se pasaron
24
- for key, value in kwargs.items():
25
- if key in plclass_map:
26
- kwargs_plfmc[key] = value
27
-
28
- self._plantinFMCreator = PlantinFMCreator.PlantinFMCreator(**kwargs_plfmc)
17
+
29
18
  #cargo el clasificador con pickle. Usamos try para capturar el error FileNotFoundError
30
19
  try:
31
20
  with open(classifier_file, 'rb') as file:
32
21
  self._pipeline = pickle.load(file)
22
+ print("Clasificador cargado con éxito.")
33
23
  except FileNotFoundError:
34
24
  print("El archivo no se encuentra en el directorio actual.")
35
25
 
36
- def classify(self, newData):
26
+ def classify(self, feature_matrix, update_samePlace:bool = True, **kwargs):
37
27
  """Genera la clasificación de las operaciones para plantines.
38
28
 
39
- newData: Es un array con los datos (strings) provenientes de la base de datos histórica. La forma de newData debe ser (n,4). Las columnas de newData deben ser,
40
- - 0: tlm_spbb son los datos de telemetría.
41
- - 1: date_oprc son los datos de fecha y hora de operación.
42
- - 2: latitud de la operación
43
- - 3: longitud de la operación
44
- - 4: precision del GPS
29
+ feature_matrix: Es un array con los datos (strings) provenientes de la base de datos histórica.
30
+ La forma de newData debe ser (n,3). Las columnas de newData deben ser,
31
+ - 1: deltaO
32
+ - 2: ratio_dCdP
33
+ - 3: distancias
34
+
35
+ kwargs: Diccionario con los argumentos necesarios para la clasificación.
36
+
37
+ NOTA: Estas características son necesarias en base a la última versión del modelo de clasificación.
45
38
  """
46
- feature_matrix = self._plantinFMCreator.fit_transform(newData)
47
- return self._pipeline.predict(feature_matrix)
39
+
40
+ self.clasificaiones = self._pipeline.predict(feature_matrix)
41
+
42
+ if update_samePlace:
43
+ self.grouped_ops = self.groupOpsSamePlace(feature_matrix, **kwargs)
44
+ self.clasificaiones = self.updateLabelsSamePlace(self.clasificaiones, self.grouped_ops)
45
+
46
+ return self.clasificaiones
48
47
 
49
- if __name__ == "__main__":
50
- from sarapy.dataProcessing import OpsProcessor
48
+ def groupOpsSamePlace(self, X, useRatioStats = True, std_weight=1, useDistancesStats = True,
49
+ ratio_dcdp_umbral=0.1, dist_umbral=0.5):
50
+ """
51
+ Función que agrupa las operaciones que se realizaron en el mismo lugar o que sean de limpieza.
52
+ Se entiende por operación en el mismo lugar aquellas operaciones que tengan distancias entre sí menores a 0.5.
53
+ La función tomará las operaciones que tengan distancias menores a 0.5 y la operación anterior, dado que se supone que la
54
+ operación anterior se corresponde a un nuevo sitio de plantado.
51
55
 
52
- #cargo archivo examples\volcado_17112023_NODE_processed.csv
53
- import pandas as pd
56
+ Las operaciones de limpieza son aquellas que tienen un ratio_dCdP menor a 0.3
57
+
58
+ Args:
59
+ - X: Array con las features de operaciones. Las columnas son deltaO, ratio_dCdP y distances.
60
+ - useRatioStats: Booleano para usar o no las estadísticas. Por defecto es True.
61
+ - std_weight: Peso para la desviación estándar. Por defecto es 1.
62
+ - ratio_dcdp_umbral: Umbral para el ratio_dCdP. Por defecto es 0.1.
63
+ - dist_umbral: Umbral para la distancia (en metros). Por defecto es 0.5.
64
+
65
+ Retorna:
66
+ - Una lista con los índices de las operaciones agrupadas.
67
+ """
68
+
69
+ if useRatioStats:
70
+ median_ratio_dcdp = np.median(X[:,1])
71
+ std_ratio_dcdp = np.std(X[:,1])
72
+ ratio_dcdp_umbral = median_ratio_dcdp - std_weight*std_ratio_dcdp
73
+
74
+ if useDistancesStats:
75
+ median_dist = np.median(X[:,2])
76
+ # std_dist = np.std(X[:,2])
77
+ dist_umbral = median_dist #- std_weight*std_dist
78
+
79
+ ##recorro las operaciones y comparo la actual con la siguiente. Si la distancia es menor a 0.5, la agrupo.
80
+ ##Si el ratio_dCdP es menor a 0.3, la agrupo.
81
+ grouped_ops = []
82
+ distancias = X[:,2]
83
+ ratio_dcdp = X[:,1]
84
+ flag_cleaning = True
85
+ for i in range(1,X.shape[0]):
86
+ if flag_cleaning:
87
+ sub_group = []
88
+ if distancias[i] < dist_umbral and ratio_dcdp[i] < ratio_dcdp_umbral:
89
+ flag_cleaning = False
90
+ sub_group.append(i-1)
91
+ sub_group.append(i)
92
+ else:
93
+ flag_cleaning = True
94
+ if len(sub_group) > 0:
95
+ grouped_ops.append(sub_group)
96
+
97
+ ##recorro grouped_ops y elimino los elementos que se repiten dentro de cada subgrupo y ordeno los indices dentro de cada subgrupo
98
+ for i in range(len(grouped_ops)):
99
+ grouped_ops[i] = list(set(grouped_ops[i]))
100
+ grouped_ops[i].sort()
101
+
102
+ return grouped_ops
103
+
104
+ def updateLabelsSamePlace(self, labels, ops_grouped):
105
+ """
106
+ Función para actualizar las etiquetas de las operaciones agrupadas en el mismo lugar.
107
+
108
+ Args:
109
+ - labels: Array con las etiquetas de las operaciones.
110
+ - indexes: Array con los índices correspondientes a operaciones repetidas
111
+ """
112
+ new_labels = labels.copy()
113
+ for indexes in ops_grouped:
114
+ new_labels[indexes[0]] = 1
115
+ new_labels[indexes[1:]] = 0
116
+
117
+ return new_labels
118
+
119
+ if __name__ == "__main__":
54
120
  import os
55
- path = os.path.join(os.getcwd(), "examples\\volcado_17112023_NODE_processed.csv")
56
- data_df = pd.read_csv(path, sep=";", )
57
- raw_data = data_df.to_numpy().astype(str)
58
-
59
- ##tomo raw_data y obtengo muestras de entre 7 a 15 filas una detrás de la otra. El valor de entre 7 y 15 es aleatorio.
60
- sample = []
61
- index = 0
62
- while True:
63
- random_value = np.random.randint(8, 15)
64
- if index + random_value < len(raw_data):
65
- sample.append(raw_data[index:index+random_value])
66
- else:
67
- break
68
- index += random_value
69
-
70
- plantin_classifier = PlantinClassifier(classifier_file="examples\\pip_lda_imp.pkl",imputeDistances = False)
71
- plantin_classifier.classify(sample[50][:,2:])
121
+ import pandas as pd
122
+ import numpy as np
123
+ from sarapy.preprocessing import TransformInputData
124
+ from sarapy.mlProcessors import PlantinFMCreator
125
+ import sarapy.utils.getRawOperations as getRawOperations
126
+ from sarapy.mlProcessors import PlantinClassifier
127
+
128
+ fmcreator = PlantinFMCreator.PlantinFMCreator(imputeDistances=False)
129
+ tindata = TransformInputData.TransformInputData()
130
+
131
+ data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM011N\\data.json")
132
+ historical_data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM011N\\historical-data.json")
133
+ raw_data = pd.read_json(data_path, orient="records").to_dict(orient="records")
134
+ raw_data2 = pd.read_json(historical_data_path, orient="records").to_dict(orient="records")
135
+
136
+ raw_ops = np.array(getRawOperations.getRawOperations(raw_data, raw_data2))
137
+ raw_X = tindata.fit_transform(raw_ops)[:,2:]
138
+
139
+ X, dst_pt, inest_pt = fmcreator.fit_transform(raw_X)
140
+
141
+ rf_clf_nu = PlantinClassifier.PlantinClassifier(classifier_file='modelos\\pipeline_rf.pkl') ##wu = no update
142
+ rf_clf_wu = PlantinClassifier.PlantinClassifier(classifier_file='modelos\\pipeline_rf.pkl') ##wu = with update
143
+
144
+ print(rf_clf_nu.classify(X, update_samePlace = False).mean())
145
+ print(rf_clf_wu.classify(X, update_samePlace=True, useRatioStats=True, useDistancesStats=True).mean())
@@ -88,7 +88,7 @@ class PlantinFMCreator(BaseEstimator, TransformerMixin):
88
88
  date_oprc = X[:,1].astype(int) #datos de fecha y hora de operación
89
89
  lats = X[:,2].astype(float) #latitudes de las operaciones
90
90
  longs = X[:,3].astype(float) #longitudes de las operaciones
91
- precitions = X[:,4].astype(float) #precision del GPS
91
+ # precitions = X[:,4].astype(float) #precision del GPS
92
92
 
93
93
  ##***** OBTENEMOS LOS DATOS PARA FITEAR LOS OBJETOS Y ASÍ PROCESAR LA FM *****
94
94
  ##obtengo las posiciones de los datos de tlmDataExtractor y timeProcessor
@@ -106,32 +106,16 @@ class PlantinFMCreator(BaseEstimator, TransformerMixin):
106
106
  ##genero un array de puntos de la forma (n,2)
107
107
  points = np.hstack((lats.reshape(-1,1),longs.reshape(-1,1)))
108
108
  self._distances = geoprocessor.fit_transform(points)
109
-
110
- ####***** IMPUTAMOS DATOS SI ES LO REQUERIDO*****
111
- if self.imputeDistances:
112
- distanceimputer = DistancesImputer.DistancesImputer(distanciaMedia = self.distanciaMedia,
113
- umbral_precision = self.umbral_precision,
114
- dist_mismo_lugar = self.dist_mismo_lugar,
115
- max_dist = self.max_dist,
116
- umbral_ratio_dCdP = self.umbral_ratio_dCdP,
117
- deltaO_medio = self.deltaO_medio, keepDims = False, columnToImpute = 0)
118
-
119
- X_distance_imputation = np.hstack((self._distances.reshape(-1, 1),
120
- precitions.reshape(-1, 1),
121
- self._tlmExtracted[:,self._tlmdeDP["GNSSFlag"]].reshape(-1, 1),
122
- self._tlmExtracted[:,self._tlmdeDP["FIX"]].reshape(-1, 1),
123
- self._timeDeltas[:,self._tpDP["deltaO"]].reshape(-1, 1),
124
- self._timeDeltas[:,self._tpDP["ratio_dCdP"]].reshape(-1, 1)))
125
-
126
- self._distances = distanceimputer.fit_transform(X_distance_imputation)
109
+
110
+ self.dst_pt = self._tlmExtracted[:,self._tlmdeDP["DSTRPT"]]
111
+ self.inest_pt = self._tlmExtracted[:,self._tlmdeDP["INESTPT"]]
127
112
 
128
113
  ##armamos la feature matrix
129
- featureMatrix = np.vstack((self._tlmExtracted[:,self._tlmdeDP["DSTRPT"]],
130
- self._timeDeltas[:,self._tpDP["deltaO"]],
131
- self._timeDeltas[:,self._tpDP["ratio_dCdP"]],
132
- self._distances)).T
114
+ self.featureMatrix = np.vstack((self._timeDeltas[:,self._tpDP["deltaO"]],
115
+ self._timeDeltas[:,self._tpDP["ratio_dCdP"]],
116
+ self._distances)).T
133
117
 
134
- return featureMatrix
118
+ return self.featureMatrix, self.dst_pt, self.inest_pt
135
119
 
136
120
  def fit_transform(self, X: np.array, y=None):
137
121
  """Fittea y transforma los datos de X en la matriz de características.
@@ -176,18 +160,23 @@ class PlantinFMCreator(BaseEstimator, TransformerMixin):
176
160
 
177
161
 
178
162
  if __name__ == "__main__":
179
- ##genero objeto FMCreator
180
- fmcreator = PlantinFMCreator(imputeDistances=False)
181
- import pandas as pd
182
163
  import os
183
- path = os.path.join(os.getcwd(), "examples\\volcado_17112023_NODE_processed.csv")
184
- raw_data = pd.read_csv(path, sep=";", ).to_numpy()
164
+ import pandas as pd
165
+ import numpy as np
166
+ from sarapy.preprocessing import TransformInputData
167
+ from sarapy.mlProcessors import PlantinFMCreator
168
+ import sarapy.utils.getRawOperations as getRawOperations
185
169
 
186
- X = raw_data[50:60,2:]
187
- fmcreator.fit(X)
188
- fm = fmcreator.fit_transform(X)
189
- print(fm)
190
- print(fm.shape)
191
-
192
- X2 = raw_data[60:81,2:]
193
- fmcreator.transform(X2).shape
170
+ fmcreator = PlantinFMCreator.PlantinFMCreator(imputeDistances=False)
171
+ tindata = TransformInputData.TransformInputData()
172
+
173
+ ##cargo los archivos examples\2024-09-04\UPM001N\data.json y examples\2024-09-04\UPM001N\historical-data.json
174
+ data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM001N\\data.json")
175
+ historical_data_path = os.path.join(os.getcwd(), "examples\\2024-09-04\\UPM001N\\historical-data.json")
176
+ raw_data = pd.read_json(data_path, orient="records").to_dict(orient="records")
177
+ raw_data2 = pd.read_json(historical_data_path, orient="records").to_dict(orient="records")
178
+
179
+ raw_ops = np.array(getRawOperations.getRawOperations(raw_data, raw_data2))
180
+ X = tindata.fit_transform(raw_ops)
181
+
182
+ fm, dst_pt, inest_pt = fmcreator.fit_transform(X[:,2:])
@@ -100,7 +100,11 @@ if __name__ == "__main__":
100
100
 
101
101
  transform_input_data = TransformInputData()
102
102
 
103
- ppk_results = getRawOperations("examples\\2024-05-30\\UPM007N\\data.json", "examples\\2024-05-30\\UPM007N\\historical-data.json")
103
+ #cargo "examples\\2024-05-30\\UPM007N\\data.json"
104
+ data = pd.read_json("examples\\2024-05-30\\UPM007N\\data.json").to_dict(orient="records")
105
+ historical_data = pd.read_json("examples\\2024-05-30\\UPM007N\\historical-data.json").to_dict(orient="records")
106
+
107
+ ppk_results = getRawOperations(data,historical_data)
104
108
 
105
109
  X = np.array(ppk_results)
106
110
  print(transform_input_data.fit_transform(X))
@@ -8,7 +8,7 @@ class TransformToOutputData(BaseEstimator, TransformerMixin):
8
8
 
9
9
  Args:
10
10
  - dataToTransform: array con los datos de las operaciones clasificadas.
11
- Actualmente el array de dataToTransform es de (n,4) con las columnas siguientes
11
+ Actualmente el array de dataToTransform es de (n,5) con las columnas siguientes
12
12
 
13
13
  - 0: id_db_h
14
14
  - 1: id_db_dw
@@ -36,6 +36,15 @@ class TransformToOutputData(BaseEstimator, TransformerMixin):
36
36
 
37
37
  def fit(self, X:np.array, y = None):
38
38
  """
39
+ Args:
40
+ - X: array con los datos de las operaciones clasificadas.
41
+ Actualmente el array de dataToTransform es de (n,5) con las columnas siguientes
42
+
43
+ - 0: id_db_h
44
+ - 1: id_db_dw
45
+ - 2: tag_seedling
46
+ - 3: tag_fertilizer
47
+ - 4: date_oprc
39
48
  """
40
49
  self.is_fitted = True
41
50
  keys = ["id_db_h", "id_db_dw", "tag_seedling", "tag_fertilizer", "date_oprc"]
@@ -45,19 +54,41 @@ class TransformToOutputData(BaseEstimator, TransformerMixin):
45
54
  date_oprc = np.array([datetime.datetime.fromtimestamp(date, datetime.timezone.utc) for date in date_data])
46
55
  self.temp_df.loc[:,"date_oprc"] = date_oprc.flatten()
47
56
  ##convierto las columnas "id_db_h", "id_db_dw", "tag_seedling", "tag_fertilizer" a int
48
- self.temp_df.loc[:,["id_db_h", "id_db_dw", "tag_seedling", "tag_fertilizer"]] = self.temp_df.loc[:,["id_db_h", "id_db_dw", "tag_seedling", "tag_fertilizer"]].astype(int)
49
-
57
+ for col in ["id_db_h", "id_db_dw", "tag_seedling", "tag_fertilizer"]:
58
+ self.temp_df[col] = self.temp_df[col].astype(float).astype(int)
59
+
50
60
  return self
51
61
 
52
62
  def transform(self, X:np.array):
53
63
  """
54
- Retorna los datos de entrada a un formato utilizable para procesar las operaciones.
64
+ Args:
65
+ - X: array con los datos de las operaciones clasificadas.
66
+ Actualmente el array de dataToTransform es de (n,5) con las columnas siguientes
67
+
68
+ - 0: id_db_h
69
+ - 1: id_db_dw
70
+ - 2: tag_seedling
71
+ - 3: tag_fertilizer
72
+ - 4: date_oprc
73
+ Returns:
74
+ Retorna una lista de diccionarios donde cada diccionario contiene los datos de una operación para los campos mencionados anteriormente.
55
75
  """
56
76
 
57
77
  return self.temp_df.to_dict(orient = "records")
58
78
 
59
79
  def fit_transform(self, X:np.array, y = None):
60
80
  """
81
+ Args:
82
+ - X: array con los datos de las operaciones clasificadas.
83
+ Actualmente el array de dataToTransform es de (n,5) con las columnas siguientes
84
+
85
+ - 0: id_db_h
86
+ - 1: id_db_dw
87
+ - 2: tag_seedling
88
+ - 3: tag_fertilizer
89
+ - 4: date_oprc
90
+ Returns:
91
+ Retorna una lista de diccionarios donde cada diccionario contiene los datos de una operación para los campos mencionados anteriormente.
61
92
  """
62
93
  self.fit(X)
63
94
  return self.transform(X)
File without changes
@@ -0,0 +1,125 @@
1
+ from dateutil import parser
2
+
3
+
4
+ """
5
+ En 'estructura_datos' se registra cuantos bits se ocupan para cada dato.
6
+ Por ejemplo, los primeros 6 bits para anio, los siguientes 4 para mes y asi.
7
+ """
8
+
9
+ estructura_datos = {
10
+ "anio": 6,
11
+ "mes": 4,
12
+ "dia": 5,
13
+ "hora": 5,
14
+ "minutos": 6,
15
+ "segundos": 6,
16
+ "operacion": 16,
17
+ "PT": 2,
18
+ "FR": 2,
19
+ "OR": 2,
20
+ "MO": 2,
21
+ "TLM_NPDP": 64,
22
+ "TLM_GPDP": 16,
23
+ "ID_NPDP": -1,
24
+ "ID_OPRR": -1,
25
+ "ID_GPDP": -1,
26
+ "ID_CDLL": -1,
27
+ "size_GNSS": 16,
28
+ "Latitud": 32,
29
+ "Longitud": 32,
30
+ "Precision": 32,
31
+ } # Agregar mas campos segun sea necesario
32
+
33
+
34
+ def extraer_bits(trama, inicio, n_bits):
35
+ try:
36
+ byte_index = inicio // 8
37
+ bit_offset = inicio % 8
38
+
39
+ valor = 0
40
+ bits_procesados = 0
41
+ while bits_procesados < n_bits:
42
+ byte_actual = trama[byte_index]
43
+ bits_restantes = n_bits - bits_procesados
44
+ bits_a_extraer = min(bits_restantes, 8 - bit_offset)
45
+
46
+ mascara = (1 << bits_a_extraer) - 1
47
+ bits_extraidos = (byte_actual >> (8 - bit_offset - bits_a_extraer)) & mascara
48
+
49
+ valor = (valor << bits_a_extraer) | bits_extraidos
50
+
51
+ bits_procesados += bits_a_extraer
52
+ byte_index += 1
53
+ bit_offset = 0
54
+
55
+ return valor
56
+ except IndexError as ex:
57
+ raise ex
58
+ except Exception as ex:
59
+ print(f"Error inesperado en extraer_bits: {ex}")
60
+ raise ex
61
+
62
+
63
+ def process_dynamic_id(trama, inicio):
64
+ # Lee el primer byte para determinar la longitud del ID
65
+ longitud_id_bytes = extraer_bits(trama, inicio, 8) # 8 bits = 1 byte
66
+ inicio += 8 # Avanza el indice de inicio 8 bits para pasar al contenido del ID
67
+
68
+ # Ahora, extrae el ID basandose en la longitud obtenida
69
+ id_value = extraer_bits(trama, inicio, longitud_id_bytes * 8) # Convierte la longitud a bits
70
+ inicio += longitud_id_bytes * 8 # Avanza el indice de inicio para pasar al final del ID
71
+
72
+ return id_value, inicio
73
+
74
+
75
+ def process_data(trama):
76
+
77
+ if not isinstance(trama, bytes):
78
+ raise ValueError("La trama debe ser un bytearray")
79
+
80
+ inicio = 0
81
+ resultado = {}
82
+ for campo, n_bits in estructura_datos.items():
83
+ try:
84
+ if n_bits == -1: # Verifica si el campo es dinamico
85
+ resultado[campo], inicio = process_dynamic_id(trama, inicio)
86
+ else:
87
+ if campo == "TLM_NPDP" or campo == "TLM_GPDP":
88
+ resultado[campo] = trama[inicio // 8: (inicio + n_bits) // 8]
89
+ else:
90
+ resultado[campo] = extraer_bits(trama, inicio, n_bits)
91
+ inicio += n_bits
92
+ if campo == "Precision":
93
+ # Suponiendo que size_GNSS sigue inmediatamente despues de Precision
94
+ raw = trama[inicio // 8: (inicio // 8 ) + resultado["size_GNSS"] - 12]
95
+ resultado["RAW"] = raw
96
+ except IndexError as ex:
97
+ print(f"Error al procesar campo {campo}: {ex}. Posiblemente la trama es mas corta de lo esperado.")
98
+ break # Salir del bucle en caso de un error de indice
99
+ except Exception as ex:
100
+ print(f"Error inesperado al procesar campo {campo}: {ex}")
101
+ break # Salir del bucle en caso de errores inesperados
102
+
103
+ if len(set(estructura_datos.keys()) - set(resultado.keys())) == 0:
104
+
105
+ anio = 2020 + resultado["anio"]
106
+ mes = str(resultado["mes"]).zfill(2)
107
+ dia = str(resultado["dia"]).zfill(2)
108
+ hora = str(resultado["hora"]).zfill(2)
109
+ minutos = str(resultado["minutos"]).zfill(2)
110
+ segundos = str(resultado["segundos"]).zfill(2)
111
+ resultado["date_oprc"] = parser.parse(f"{anio}-{mes}-{dia}T{hora}:{minutos}:{segundos}+00:00")
112
+
113
+ resultado["Latitud"] = (resultado["Latitud"] - 2 ** 32) / 10 ** 7
114
+ resultado["Longitud"] = (resultado["Longitud"] - 2 ** 32) / 10 ** 7
115
+
116
+ del resultado["anio"]
117
+ del resultado["mes"]
118
+ del resultado["dia"]
119
+ del resultado["hora"]
120
+ del resultado["minutos"]
121
+ del resultado["segundos"]
122
+ del resultado["size_GNSS"]
123
+
124
+ return resultado
125
+
@@ -0,0 +1,38 @@
1
+ from base64 import b64decode
2
+
3
+ from sarapy.utils import amg_decoder
4
+
5
+
6
+ def main(hash_table, ppk_data):
7
+
8
+ ppk_results = []
9
+
10
+ for hash_table_entry_values in hash_table.values():
11
+
12
+ try:
13
+
14
+ serialized_datum = hash_table_entry_values["serialized_datum"]
15
+ raw_datum = bytes(b64decode(serialized_datum.encode("utf-8"))) # 'trama'
16
+ datum = amg_decoder.process_data(raw_datum)
17
+
18
+ if datum:
19
+
20
+ longitude, latitude, accuracy = "", "", 0 # ToDo: PPK (Fernando)
21
+
22
+ if longitude:
23
+ datum["Longitud"] = longitude
24
+ if latitude:
25
+ datum["Latitud"] = latitude
26
+ if accuracy != 0:
27
+ datum["Precision"] = accuracy
28
+
29
+ ppk_results.append({
30
+ "id_db_dw": hash_table_entry_values["id_db_dw"],
31
+ "id_db_h": hash_table_entry_values["id_db_h"],
32
+ **datum
33
+ })
34
+
35
+ except Exception as ex:
36
+ print(ex)
37
+
38
+ return ppk_results
@@ -0,0 +1,20 @@
1
+ import pandas as pd
2
+ from sarapy.utils import amg_ppk
3
+ import os
4
+ def getRawOperations(data, historical_data):
5
+ """
6
+ Args:
7
+ data_file: Lista de diccionarios con la data
8
+ historical_data_file: Lista de diccionarios con historical_data
9
+
10
+ Returns the raw operations from the database.
11
+ """
12
+ hash_table = {}
13
+ for datum in data:
14
+ hash_table[datum["timestamp"]] = {"id_db_dw": datum["id"], "id_db_h": 0, "serialized_datum": ""}
15
+ for historical_datum in historical_data:
16
+ if historical_datum["timestamp"] in hash_table:
17
+ hash_table[historical_datum["timestamp"]].update({"id_db_h": historical_datum["id"], "serialized_datum": historical_datum["datum"]})
18
+ ppk_results = amg_ppk.main(hash_table, []) # ToDo: PPK (Fernando)
19
+
20
+ return ppk_results
sarapy/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  ## Version of the package
2
- __version__ = "1.0.0"
2
+ __version__ = "1.1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sarapy
3
- Version: 1.0.0
3
+ Version: 1.1.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,17 @@ Requires-Dist: geopy
19
19
 
20
20
  Library for processing SARAPICO project metadata of _AMG_.
21
21
 
22
+ #### Version 1.1.0
23
+
24
+ Versión 1.1 estable para trabajarse en servidor.
25
+
26
+ - Se implementa nueva estrategia para la clasificación de plantines.
27
+
28
+
29
+ #### Version 1.0.1
30
+
31
+ - Se agrega *__init.py__* dentro del mpdulo _utils_.
32
+
22
33
  #### Version 1.0.0
23
34
 
24
35
  - Se crean clases TransformInputData y TransformToOuputData dentro del módulo proprocessing. Se crean utils.
@@ -0,0 +1,25 @@
1
+ sarapy/__init__.py,sha256=aVoywqGSscYYDycLaYJnz08dlQabl9gH0h4Q5KtHM9o,74
2
+ sarapy/version.py,sha256=s61q4qMKCUuPCPfTUqwvvIijwHBDiVMOFSU6VmU1ArI,51
3
+ sarapy/dataProcessing/GeoProcessor.py,sha256=YeNFHZ5sArnSLDxz009SJzgdWuMPgvJPkqtwcivk87A,4654
4
+ sarapy/dataProcessing/OpsProcessor.py,sha256=-mxZPA7V6r7xkkQBICmNBiT7FJGVs3C3D-DaPN34yHE,16433
5
+ sarapy/dataProcessing/TLMSensorDataProcessor.py,sha256=GfSIRYD_biFlOMTfSQSwW0HsUouZuUL3ScvL4uUHTPQ,23824
6
+ sarapy/dataProcessing/TimeSeriesProcessor.py,sha256=QPvg9vyPSWjVl020zbebqvlM9JnL1uEAuACRsfZweAg,5497
7
+ sarapy/dataProcessing/__init__.py,sha256=Kqs5sFtq6RMEa3KLJFbsGRoYsIxHL1UUGMuplyCyQFk,200
8
+ sarapy/dataProcessing/amg_decoder.py,sha256=JZ7cbu7DlCuatuq2F7aBfUr7S7U-K5poBgxw5nY6rNI,4319
9
+ sarapy/mlProcessors/PlantinClassifier.py,sha256=ahoDIgxfKiq0sMm4dR1M4rLO-J6SxDMrASjlmswj3QI,6781
10
+ sarapy/mlProcessors/PlantinFMCreator.py,sha256=PiDFCW7LQ00OTT0lqHwv03yVfqylHYz-7fPCxWnx5yk,8999
11
+ sarapy/mlProcessors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ sarapy/preprocessing/DistancesImputer.py,sha256=NvbVAh5m0yFxVgDbEFnEX7RSG13qLjO7i2gqjDAWsf4,9106
13
+ sarapy/preprocessing/FertilizerImputer.py,sha256=zK6ONAilwPHvj-bC7yxnQYOkDBCCkWh6__57vYK9anM,1490
14
+ sarapy/preprocessing/TransformInputData.py,sha256=wDwyg4U-jgTqvNUFngsmhSKwyhoIyVWlhezEhhPf2qE,4090
15
+ sarapy/preprocessing/TransformToOutputData.py,sha256=REQX1d57bCqS3l9dh1m7ybJ-WSX1BGFzweojWMHar8A,3788
16
+ sarapy/preprocessing/__init__.py,sha256=Wg_Csy8Xiz8BN8A4-T7iPwcL_ol5ApEx6YtybItKB8M,100
17
+ sarapy/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ sarapy/utils/amg_decoder.py,sha256=JZ7cbu7DlCuatuq2F7aBfUr7S7U-K5poBgxw5nY6rNI,4319
19
+ sarapy/utils/amg_ppk.py,sha256=c0GusnxdntU-E0JOezzbIfC7SWoJmKAbad_zYDCJ3-c,1060
20
+ sarapy/utils/getRawOperations.py,sha256=8aA1fIkNCnUYgiWfnFggRT_U35z432gZBrZ7seGO5w4,817
21
+ sarapy-1.1.0.dist-info/LICENCE,sha256=N00sU3vSQ6F5c2vML9_qP4IFTkCPFFj0YGDB2CZP-uQ,840
22
+ sarapy-1.1.0.dist-info/METADATA,sha256=oIu6w2r9Yj0EH0aiYLIPmTLarsGTK9ZpqKBXYbGDHWw,2568
23
+ sarapy-1.1.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
24
+ sarapy-1.1.0.dist-info/top_level.txt,sha256=4mUGZXfX2Fw47fpY6MQkaJeuOs_8tbjLkkNp34DJWiA,7
25
+ sarapy-1.1.0.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- sarapy/__init__.py,sha256=aVoywqGSscYYDycLaYJnz08dlQabl9gH0h4Q5KtHM9o,74
2
- sarapy/version.py,sha256=BwwbbQmaXV3f3NFM7cC4RWEuaju9GRSv_mfol3KcUwg,51
3
- sarapy/dataProcessing/GeoProcessor.py,sha256=YeNFHZ5sArnSLDxz009SJzgdWuMPgvJPkqtwcivk87A,4654
4
- sarapy/dataProcessing/OpsProcessor.py,sha256=NNEI2F6TtaxU8sHAr3yjo7nfsZ4hlGx8umb6VDnrPa4,14607
5
- sarapy/dataProcessing/TLMSensorDataProcessor.py,sha256=GfSIRYD_biFlOMTfSQSwW0HsUouZuUL3ScvL4uUHTPQ,23824
6
- sarapy/dataProcessing/TimeSeriesProcessor.py,sha256=QPvg9vyPSWjVl020zbebqvlM9JnL1uEAuACRsfZweAg,5497
7
- sarapy/dataProcessing/__init__.py,sha256=Kqs5sFtq6RMEa3KLJFbsGRoYsIxHL1UUGMuplyCyQFk,200
8
- sarapy/dataProcessing/amg_decoder.py,sha256=JZ7cbu7DlCuatuq2F7aBfUr7S7U-K5poBgxw5nY6rNI,4319
9
- sarapy/mlProcessors/PlantinClassifier.py,sha256=KI8xqBTsJb1dwNgqM5Z8w1kgU74ahwJSJ5CHbYfIlG8,3289
10
- sarapy/mlProcessors/PlantinFMCreator.py,sha256=5E3ueTdluGsBfDiDVilQCbxnDH6Y9OxQIbFokB83fy0,9751
11
- sarapy/mlProcessors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- sarapy/preprocessing/DistancesImputer.py,sha256=NvbVAh5m0yFxVgDbEFnEX7RSG13qLjO7i2gqjDAWsf4,9106
13
- sarapy/preprocessing/FertilizerImputer.py,sha256=zK6ONAilwPHvj-bC7yxnQYOkDBCCkWh6__57vYK9anM,1490
14
- sarapy/preprocessing/TransformInputData.py,sha256=UPdb9vQpMBLQNZX_4tP2bl3s-nQ5y3oDgQ8kYr0rzlA,3898
15
- sarapy/preprocessing/TransformToOutputData.py,sha256=h4QsIFYde4w-8UxdQvNYpb5VTFeCz1oRwgxK3Rs44nI,2454
16
- sarapy/preprocessing/__init__.py,sha256=Wg_Csy8Xiz8BN8A4-T7iPwcL_ol5ApEx6YtybItKB8M,100
17
- sarapy-1.0.0.dist-info/LICENCE,sha256=N00sU3vSQ6F5c2vML9_qP4IFTkCPFFj0YGDB2CZP-uQ,840
18
- sarapy-1.0.0.dist-info/METADATA,sha256=umG5dvUMzbpgf2spOAxO4Z2o_5xAzjniecOZpOT4ayQ,2340
19
- sarapy-1.0.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
20
- sarapy-1.0.0.dist-info/top_level.txt,sha256=4mUGZXfX2Fw47fpY6MQkaJeuOs_8tbjLkkNp34DJWiA,7
21
- sarapy-1.0.0.dist-info/RECORD,,
File without changes