Introducción
En el desarrollo de aplicaciones de Internet de las Cosas (IoT), el manejo eficiente de los flujos de datos provenientes de sensores es crucial. La biblioteca estándar de plantillas (STL) de C++ ofrece un conjunto robusto de algoritmos genéricos que son ideales para operaciones comunes de transformación, filtrado y análisis de datos. Este artículo explora cómo aplicar estos algoritmos clave para el preprocesamiento de datos en escenarios típicos de IoT.
- Algoritmos de consulta sin modificación
Estos algoritmos inspeccionan los datos sin alterarlos, siendo útiles para la detección de patrones, validación y extracción de información básica.
1.1 Búsqueda de valores y patrones
Para localizar elementos específicos o que cumplan condiciones dentro de un vector de datos.
#include <algorithm>
#include <vector>
#include <iostream>
// Datos simulados de sensores de temperatura
std::vector<double> lecturas_temperatura = {22.5, 23.1, 18.7, 25.0, 19.3, 22.5};
// Buscar la primera lectura igual a 22.5
auto it = std::find(lecturas_temperatura.begin(), lecturas_temperatura.end(), 22.5);
if (it != lecturas_temperatura.end()) {
std::cout << "Lectura 22.5 encontrada en el índice: "
<< std::distance(lecturas_temperatura.begin(), it) << std::endl;
}
// Buscar la primera lectura por encima de un umbral (ej. 24.0)
auto it_umbral = std::find_if(lecturas_temperatura.begin(), lecturas_temperatura.end(),
[](double temp) { return temp > 24.0; });
if (it_umbral != lecturas_temperatura.end()) {
std::cout << "Primera lectura anormal: " << *it_umbral << std::endl;
}
1.2 Conteo de ocurrencias
Para cuantificar eventos específicos, como lecturas fuera de rango.
std::vector<int> niveles_humedad = {45, 62, 78, 95, 50, 92, 88};
// Contar cuántas lecturas exceden un nivel crítico (ej. 90)
int contador_critico = std::count_if(niveles_humedad.begin(), niveles_humedad.end(),
[](int nivel) { return nivel > 90; });
std::cout << "Lecturas críticas de humedad: " << contador_critico << std::endl; // Salida: 2
1.3 Validación y comparación
Para verificar la integridad de los datos o comparar dos conjuntos de mediciones.
std::vector<int> valores_calibrados = {100, 200, 300, 400};
std::vector<int> valores_actuales = {100, 200, 350, 400};
// Verificar si todos los valores actuales están dentro de un margen del 10% de los calibrados
bool dentro_margen = std::equal(valores_actuales.begin(), valores_actuales.end(),
valores_calibrados.begin(),
[](int actual, int calibrado) {
double tolerancia = 0.1 * calibrado;
return std::abs(actual - calibrado) <= tolerancia;
});
std::cout << "¿Lecturas dentro de tolerancia? " << (dentro_margen ? "Sí" : "No") << std::endl;
- Algoritmos de transformación de secuencias
Estos algoritmos modifican los datos, realizando tareas como limpieza, normalización o cálculo de métricas derivadas.
2.1 Transformación y mapeo
Para aplicar una función a cada elemento, útil para conversiones de unidades o cálculos.
#include <numeric>
std::vector<double> corrientes_mA = {120.5, 85.2, 200.1, 150.8};
std::vector<double> potencias_W(corrientes_mA.size());
const double voltaje = 12.0;
// Calcular potencia (P = V * I) para cada lectura
std::transform(corrientes_mA.begin(), corrientes_mA.end(), potencias_W.begin(),
[voltaje](double corriente) { return voltaje * (corriente / 1000.0); });
// Calcular la potencia total consumida usando 'accumulate'
double potencia_total = std::accumulate(potencias_W.begin(), potencias_W.end(), 0.0);
2.2 Filtrado y remoción
Para eliminar datos no deseados o corruptos de la secuencia.
std::vector<double> valores_crudos = {1.02, -0.5, 1.05, 1.01, 100.0, 0.98};
// Eliminar valores negativos y aquellos claramente erróneos (ej. > 50)
valores_crudos.erase(
std::remove_if(valores_crudos.begin(), valores_crudos.end(),
[](double v) { return v < 0 || v > 50; }),
valores_crudos.end()
);
// Ahora 'valores_crudos' solo contiene {1.02, 1.05, 1.01, 0.98}
2.3 Ordenamiento y búsqueda eficiente
Para organizar datos y realizar búsquedas rápidas después de la adquisición.
#include <algorithm>
std::vector<int> ids_dispositivos = {502, 104, 301, 207, 104, 502};
// Primero, ordenar y eliminar duplicados para tener una lista única
std::sort(ids_dispositivos.begin(), ids_dispositivos.end());
auto fin_unicos = std::unique(ids_dispositivos.begin(), ids_dispositivos.end());
ids_dispositivos.erase(fin_unicos, ids_dispositivos.end());
// ids_dispositivos ahora es {104, 207, 301, 502}
// Luego, realizar una búsqueda binaria para comprobar existencia rápida
int id_buscado = 207;
bool existe = std::binary_search(ids_dispositivos.begin(), ids_dispositivos.end(), id_buscado);
- Algoritmos numéricos
Funciones de la cabecera <numeric> específicas para cálculos matemáticos comunes en series temporales de sensores.
3.1 Acumulación y producto interior
#include <numeric>
std::vector<double> muestras_vibracion = {0.1, 0.3, 0.2, 0.5, 0.4};
// Calcular la energía de la vibración (suma de cuadrados)
double energia = std::inner_product(muestras_vibracion.begin(), muestras_vibracion.end(),
muestras_vibracion.begin(), 0.0);
// Calcular la media móvil usando 'partial_sum' y división
std::vector<double> suma_movil(muestras_vibracion.size());
std::partial_sum(muestras_vibracion.begin(), muestras_vibracion.end(), suma_movil.begin());
// Para obtener la media móvil de ventana 3, se puede usar (suma_movil[i] - suma_movil[i-3]) / 3.0
3.2 Generación de secuencias
Para inicializar buffers de datos o crear índices temporales.
#include <numeric>
// Generar marcas de tiempo (timestamps) en intervalos de 5 segundos
std::vector<unsigned long> timestamps(10);
unsigned long inicio = 1672531200; // Ejemplo de epoch
std::iota(timestamps.begin(), timestamps.end(), inicio);
// Ahora timestamps contiene {inicio, inicio+1, inicio+2, ...} (representando segundos)
Consideraciones de rendimiento
Al trabajar con datos de IoT que pueden llegar en tiempo real, es importante considerar la complejidad algerítmica. Los algoritmos de ordenamiento como std::sort son O(n log n), mientras que búsquedas lineales con std::find son O(n). Para conjuntos de datos estáticos y ordenados, std::binary_search ofrece búsquedas O(log n). La elección del algoritmo adecuado impacta directamente la eficiencia del sistema embebido.