4.4 Rssi vs Distancia. Linealiza POR intervalos – Algoritmo Python

El algoritmo para realizar dos o más intervalos considera usar un estimado de frontera a lo largo de las mediciones.

Se implementa añadiendo al bloque de ingreso dos parámetros en el bloque ‘analiza’:

1. frontera es un vector donde se indican las distancia de los puntos de corte de los subintervalos sin considerar los extremos, corresponden a la frontera estimada en el mapa. El algoritmo actualiza la frontera con la intersección  de la linealización de dos subintervalos consecutivos. Si frontera es un vector vacío [] se asume que se trabaja con todo el intervalo.

2. ‘atipInterv_std’ es un vector para la discriminación de los valores atipicos aplicada en cada subintervalo.

Como referencia para comparar, el resultado del análisis de todo el intervalo se denomina ‘r0’. Los subintervalos se identifican por ‘r1′,’r2’, etc, en orden al alejarse de la baliza.

En el resultado como valor complementario de revisión se añade el coeficiente de correlación de los puntos usados en el análisis.

Algoritmo en Python

# LoRa-Multipunto, Rssi vs distancia
# linealización Rssi vs log10(distancia)
# por mínimos cuadrados
# Graficas 2D y 3D
# Girni 2020-10-07 propuesta: edelros@espol.edu.ec

import numpy as np
import pandas as pd
import json
import matplotlib.pyplot as plt
import girni_lora_libreria as girni

# INGRESO
# archivos de entrada
modo       = 'rx'
medida     = 'rssi'
descriptor = 'mean'
arch_medidaubica = 'rsmP06_'+medida+'Ubica01Intervalo.txt'

# archivos de salida
arch_ecuaciones  = 'rsmP07_ecuacion01Intervalos.json'

# Analizar por segmentos
analiza = {'gtwRECT':{'analizar'  : 1,
                      'atipico_std' : 1,
                      'frontera'    : [320],
                      'atipInterv_std': [1,1],
                      'p_amplia': 4,
                      'grp' : ['RECT','FIEC'],
                      'tip' : ['punto'],
                      'LOS' : [1]},
           'gtwFIEC':{'analizar'  : 1,
                      'atipico_std' : 1,
                      'frontera'    : [190], 
                      'atipInterv_std': [1,1],
                      'p_amplia': 4,
                      'grp' : ['FIEC','FCNM'],
                      'tip' : ['punto'],
                      'LOS' : [1,0]},
           'gtwFCNM':{'analizar'   : 1,
                      'atipico_std' : 1,
                      'frontera'   : [235.0],
                      'atipInterv_std': [2,2],
                      'p_amplia': 4,
                      'grp' : ['FIEC','FCNM'],
                      'tip' : ['punto'],
                      'LOS' : [1,0]}
           }

baliza = {'d1':'gtwRECT',
          'd2':'gtwFIEC',
          'd3':'gtwFCNM'}

# Parámetros de grafica
tipograf      = '2D'  # '2D','3D'
escala        = 'log' # 'normal','log'
escalabase    = 10    # 10, np.exp()
casicero   = 1e-4
precision  = 3
intersectar = 1 # 0:Falso, 1: Verdadero

# Referencias de gráfica
grupo   = ['FIEC' ,'FCNM'  ,'RECT','CIRC']
colores = ['green','orange','grey','magenta']
tipo    = ['punto','1m' ,'gtw','dispositivo']
marcas  = [    'o','D'  ,'D'  ,'*' ]

mostrargrpeti = ['FIEC','FCNM','RECT']
mostrartipeti = ['1m','gtw']

# PROCEDIMIENTO
# Resultados de análisis
ecuacion = {}
eq_graf  = {}

# leer datos
tabla = pd.read_csv(arch_medidaubica, index_col='etiqueta')
tabla = pd.DataFrame(tabla)

# analiza datos hacia una baliza
for unabaliza in analiza:

    # Parámetros
    analizar = analiza[unabaliza]['analizar']

    if analizar:
        ecuacion[unabaliza] = {}
        eq_graf[unabaliza]  = {}
        
        # pares a usar de baliza
        [pares,par_etiqueta] = girni.pares_usar(tabla,baliza,
                                            analiza,unabaliza,
                                            medida = medida ,
                                            modo = modo)
        # todos los puntos
        # analiza puntos para mínimos cuadrados
        xi = pares[:,0]
        yi = pares[:,1]
        n_xi = len(xi)
        # coeficiente de correlación
        correlacion = np.corrcoef(xi,yi)[0,1]
        
        # minimos cuadrados
        ecuacion0 = girni.linealiza_lstsq(xi,yi)

        # selecciona atipicos
        atipico_std = analiza[unabaliza]['atipico_std']
        alpha    = ecuacion0['alpha']
        beta     = ecuacion0['beta']
        fdist0   = lambda d: -10*alpha*(np.log10(d))+beta
        yi0      = fdist0(xi)
        dyi0std  = ecuacion0['error_std']
        dyi0     = yi - yi0
        atipicos = np.abs(dyi0) >= dyi0std*atipico_std
        xi0_e   = xi[atipicos]
        yi0_e   = yi[atipicos]
        etiq0_e = par_etiqueta[atipicos]

        unintervalo = 'r0' # todos
        # para exportar hacia archivo o gráfica
        ecuacion[unabaliza] = {unintervalo: ecuacion0}
        ecuacion[unabaliza][unintervalo]['correlacion'] = correlacion
        
        eq_graf[unabaliza]  = {unintervalo: {'xi_graf':xi,
                                         'yi_graf':yi,
                                         'etiqueta': par_etiqueta,
                                         'linea' : yi0,
                                         'atipicos':[xi0_e,yi0_e],
                                         'atip_etiq': etiq0_e}
                               }
        
        # Intervalos radiales en sector
        intervalo = [np.min(xi),np.max(xi)]
        frontera = analiza[unabaliza]['frontera']
        if len(frontera)>0:
            # revisar si frontera esta dentro intervalo
            frontera = np.array(frontera, dtype=float)
            revisar  = (frontera>=np.min(xi)) & (frontera<=np.max(xi))
            enintervalo = list(frontera[revisar])
            intervalo.extend(enintervalo)
            intervalo = np.array(intervalo)
            ordenar   = np.argsort(intervalo)
            intervalo = intervalo[ordenar]
        n_intervalo = len(intervalo)
        
        # analizar cada subintervalo
        p_inicio = 0
        p_desde  = 0
        p_amplia = analiza[unabaliza]['p_amplia']
        atipIntv_std = analiza[unabaliza]['atipInterv_std']
        for i in range(0,n_intervalo-1,1):
            i_eq = 'r'+str(i+1)  # indice en texto

            # puntos en subintervalo [a,b]
            a = intervalo[i]
            b = intervalo[i+1]
            subintervalo = (xi >= a) & (xi <= b)  
            xi_sub = xi[subintervalo]
            yi_sub = yi[subintervalo]
            n_sub  = len(xi_sub)
            etiq_sub = par_etiqueta[p_inicio:p_inicio + n_sub]

            # amplia sub-intervalo, mejora intersecta rectas
            detras    = p_inicio
            retrocede = detras
            if detras > p_amplia:
                retrocede = p_amplia
            delante = n_xi - (p_inicio+n_sub)# -1)
            avanza  = delante
            if delante >= p_amplia:
                avanza = p_amplia
            p_desde  = p_inicio - retrocede
            p_hasta  = p_inicio + (n_sub) + avanza
            p_inicio = p_inicio + (n_sub-1)
            
            # subintervalo, amplia puntos
            xi_a = xi[p_desde:p_hasta]
            yi_a = yi[p_desde:p_hasta]
            etiq_a = par_etiqueta[p_desde:p_hasta]
            # coeficiente de correlación
            correlacion1 = np.corrcoef(xi_a,yi_a)[0,1]

            # analiza subintervalo
            ecuacion1 = girni.linealiza_lstsq(xi_a,yi_a)
            ecuacion[unabaliza][i_eq] = ecuacion1
            ecuacion[unabaliza][i_eq]['correlacion'] = correlacion1  
            # atipicos del subintervalo extendido
            alpha  = ecuacion1['alpha']
            beta   = ecuacion1['beta']
            fdist1 = lambda d: -10*alpha*(np.log10(d))+beta
            yi1  = fdist1(xi_a)
            dyi1std = ecuacion1['error_std']
            atipico_std = analiza[unabaliza]['atipInterv_std'][i]
            dyi1 = yi_a - yi1
            atipicos = np.zeros(len(xi_a),dtype=bool)
            if np.abs(dyi1std) > casicero:
                atipicos = np.abs(dyi1) >= dyi1std*atipico_std
            xi1_e = xi_a[atipicos]
            yi1_e = yi_a[atipicos]
            etiq1_e = etiq_a[atipicos]

            # para gráfica, atipicos sin extender puntos 
            atipicos_sub = (xi1_e >= a) & (xi1_e<=b)
            xi_sub1_e = xi1_e[atipicos_sub]
            yi_sub1_e = yi1_e[atipicos_sub]
            etiq_sub1e = etiq1_e[atipicos_sub]
            eq_graf[unabaliza][i_eq] = {'atipicos': [xi_sub1_e,yi_sub1_e],
                                        'atip_etiq':etiq_sub1e}
            

            # subintervalo sin atipicos
            if len(xi1_e)>0:
                atipicoNo = np.abs(dyi1) <= dyi1std*atipico_std
                xi2 = xi_a[atipicoNo]
                yi2 = yi_a[atipicoNo]
                etiq2 = etiq_a[atipicoNo]
                # coeficiente de correlación
                correlacion2 = np.corrcoef(xi2,yi2)[0,1]
                ecuacion2 = girni.linealiza_lstsq(xi2,yi2)

                # actualiza ecuación sin atipicos intervaloy
                intervalox = ecuacion1['intervalox']
                ecuacion2['intervalox'] = intervalox.copy()
                alpha = ecuacion2['alpha']
                beta  = ecuacion2['beta']
                fdist = lambda d: -10*alpha*(np.log10(d))+beta
                intervaloy = fdist(intervalox)
                ordenar   = np.argsort(intervaloy)
                intervaloy = list(intervaloy[ordenar])
                ecuacion2['intervaloy'] = intervaloy
                ecuacion[unabaliza][i_eq] = ecuacion2
                #ecuacion[unabaliza][i_eq]['correlacion1'] = correlacion1
                ecuacion[unabaliza][i_eq]['correlacion'] = correlacion2
            
        # Revisa frontera entre subintervalos,
        # usa intersección de rectas como nueva frontera
        interv_calc = np.copy(intervalo)
        if len(intervalo) >2 and intersectar==1 : 
            for i in range(0,n_intervalo-2,1):
                ai = 'r'+str(i+1)
                bi = 'r'+str(i+2)
                ma = ecuacion[unabaliza][ai]['alpha']
                ba = ecuacion[unabaliza][ai]['beta']
                mb = ecuacion[unabaliza][bi]['alpha']
                bb = ecuacion[unabaliza][bi]['beta']

                # punto de intersección o cruce
                cruzanx = 10**((bb-ba)/(10*(mb-ma)))
                dfrontera = frontera-cruzanx
                # cruce dentro de intervalo de ecuacion
                if cruzanx > intervalo[-1]:
                    cruzanx = intervalo[-1]
                if cruzanx < intervalo[0]:
                    cruzanx = intervalo[0]
                interv_calc[i+1] = cruzanx
            
        # para grafica evalua cada subintervalo sin atipicos
        n_interv_calc = len(interv_calc)
        for i in range(0,n_interv_calc-1,1):
            i_eq = 'r'+str(i+1)
            a = interv_calc[i]
            b = interv_calc[i+1]
            subintervalo = (xi >= a) & (xi <= b)
            xi_sub = xi[subintervalo]
            yi_sub = yi[subintervalo]
            xi_graf = np.copy(xi[subintervalo])
            if not(a in xi_sub):
                xi_graf = np.concatenate(([a],xi_graf),axis=0)
            if not(b in xi_sub):
                xi_graf = np.concatenate((xi_graf,[b]),axis=0)
            
            # Evalua subintervalo con la ecuacion sin atipicos
            alpha = ecuacion[unabaliza][i_eq]['alpha']
            beta  = ecuacion[unabaliza][i_eq]['beta']
            fdist = lambda d: -10*alpha*(np.log10(d))+beta
            yi1_sub = fdist(xi_sub)
            yi_graf = fdist(xi_graf)
            eq_graf[unabaliza][i_eq]['xi_graf'] = xi_graf
            eq_graf[unabaliza][i_eq]['yi_graf'] = yi_graf
            a = np.round(np.min([xi_graf]),precision)
            b = np.round(np.max([xi_graf]),precision)
            ecuacion[unabaliza][i_eq]['intervalox'] = [a,b]
            ay = np.round(np.min([yi_graf]),precision)
            by = np.round(np.max([yi_graf]),precision)
            ecuacion[unabaliza][i_eq]['intervaloy'] = [ay,by]            
             
# SALIDA
for unabaliza in ecuacion:
    print('baliza: ',unabaliza)
    for unaecuacion in ecuacion[unabaliza]:
        unintervalo  = ecuacion[unabaliza][unaecuacion]['intervalox']
        unintervaloy = ecuacion[unabaliza][unaecuacion]['intervaloy']
        error_medio  = ecuacion[unabaliza][unaecuacion]['error_medio']
        error_std    = ecuacion[unabaliza][unaecuacion]['error_std']
        eq_latex     = ecuacion[unabaliza][unaecuacion]['eq_latex']
        errorx_medio = ecuacion[unabaliza][unaecuacion]['errorx_medio']
        errorx_std   = ecuacion[unabaliza][unaecuacion]['errorx_std']
        correlacion  = ecuacion[unabaliza][unaecuacion]['correlacion']

        print('  intervalo: ',unaecuacion)
        print('    ' + eq_latex)
        print('   ','intervalox: ',np.round(unintervalo,precision))
        print('   ','intervaloy: ',np.round(unintervaloy,precision))
        print('    correlación: ',np.round(correlacion,precision))
        print('    |error_rssi| promedio: ',np.round(error_medio,precision),
              ' , std:',np.round(error_std,precision))
        print('    |error_dist| promedio: ',np.round(errorx_medio,precision),
              ' , std:',np.round(errorx_std,precision))
    print()

# salida hacia archivo
with open(arch_ecuaciones, 'w') as outfile:
    json.dump(ecuacion, outfile) 

# GRAFICA
# Referencias para gráfica
grupo   = ['FIEC' ,'FCNM'  ,'RECT','CIRC']
colores = ['green','orange','grey','magenta']
tipo    = ['punto','1m' ,'gtw','dispositivo']
marcas  = [    'o','D'  ,'D'  ,'*' ]

mostrargrpeti = ['FIEC','FCNM','RECT']
mostrartipeti = ['1m','gtw']

if tipograf=='2D':
    for unabaliza in ecuacion:
        figura,grafica = plt.subplots()
        if escala == 'log':
            grafica.set_xscale(escala,base=escalabase)

        # todos los puntos
        unintervalo = 'r0'
        xi = eq_graf[unabaliza][unintervalo]['xi_graf']
        yi = eq_graf[unabaliza][unintervalo]['yi_graf']
        etiqueta = eq_graf[unabaliza][unintervalo]['etiqueta']
        grafica.scatter(xi,yi,marker='.')
        m = len(xi)
        for i in range(0,m,1):
            grafica.annotate(etiqueta[i],
                            (xi[i],yi[i]))
            
        # linea con todos los puntos
        fdtxt = ecuacion[unabaliza][unintervalo]['eq_latex']
        yi0 = eq_graf[unabaliza][unintervalo]['linea']
        a = np.round(np.min([xi]),2)
        b = np.round(np.max([xi]),2)
        eq_texto = fdtxt+' ; ['+str(a)+','+str(b)+']'
        grafica.plot(xi,yi0,
                     label = eq_texto,
                     linestyle='dotted')

        # lineas por cada subintervalo
        eq_interv = list(ecuacion[unabaliza].keys())
        eq_interv.pop(0)
        n_intervalo = len(eq_interv)

        for i_eq in eq_interv:
            fdtxt   = ecuacion[unabaliza][i_eq]['eq_latex']
            xi_graf = eq_graf[unabaliza][i_eq]['xi_graf']
            yi_graf = eq_graf[unabaliza][i_eq]['yi_graf']
            a = np.round(np.min([xi_graf]),precision)
            b = np.round(np.max([xi_graf]),precision)
            eq_texto = fdtxt+' ; ['+str(a)+','+str(b)+']'
            grafica.plot(xi_graf,yi_graf,
                         label = eq_texto)

            # atipicos marcados en subintervalo
            [xi1_e,yi1_e] = eq_graf[unabaliza][i_eq]['atipicos']
            etiq1_e = eq_graf[unabaliza][i_eq]['atip_etiq']
            grafica.scatter(xi1_e,yi1_e, color='red')
            m = len(etiq1_e)
            for i in range(0,m,1):
                grafica.annotate(etiq1_e[i],
                                (xi1_e[i],yi1_e[i]),
                                 color='red')
        
            # lineas de frontera
            grafica.axvline(a, color='lightblue')
            valor_frontera = str(np.round(a,precision))
            grafica.annotate(valor_frontera,
                             (a,np.max([yi,yi0])),
                             color='lightblue')
            grafica.axvline(b, color='lightblue')
            valor_frontera = str(np.round(b,precision))
            grafica.annotate(valor_frontera,
                             (b,np.max([yi,yi0])),
                             color='lightblue') 
        
        # etiquetas y títulos
        grafica.legend()
        grafica.set_ylabel(medida+'_'+modo)
        grafica.set_xlabel('distancia')
        grafica.grid(True,linestyle='dotted',
                     axis='x', which='both')
        
        untitulo = unabaliza+': '+medida+'_'+modo + ' vs distancia'
        grafica.set_title(untitulo)
        
        plt.show()