text: #-----------------------------------------------------------------------------------------
  # @Autor: Aurélien Vannieuwenhuyze
  # @Empresa: Junior Makers Place
  # @Libro
  # @Capítulo: 03 - Estadísticas para comprender los datos
  #
  # Módulos necesarios:
  #   PANDAS 0.24.2
  #   NUMPY 1.16.3
  #   MATPLOTLIB 3.1.0
  #
  # Para instalar un módulo:
  #   Haga clic en el menú File > Settings > Project:nombre_del_proyecto > Project interpreter > botón +
  #   Introduzca el nombre del módulo en la zona de búsqueda situada en la parte superior izquierda
  #   Elegir la versión en la parte inferior derecha
  #   Haga clic en el botón install situado en la parte inferior izquierda
  #-----------------------------------------------------------------------------------------
  
  
  
  
  from collections import Counter
  from math import *
  import matplotlib.pyplot as plt
  
  
  
  class JMPEstadisticas:
  
      def __init__(self,caracteristica):
          self.caracteristica = caracteristica
  
  
      def calculoMediaAritmetica(self):
  
          n = self.caracteristica.count()
          sumaValoresObservaciones = 0
          mediaAritmetica = 0
          for valorObservacion in self.caracteristica:
              sumaValoresObservaciones = sumaValoresObservaciones + valorObservacion
  
          mediaAritmetica = sumaValoresObservaciones / n
          return mediaAritmetica
  
      def calculoMediana(self):
          mediana = 0
          caracteristica = self.caracteristica.sort_values()
          caracteristica = caracteristica.reset_index(drop=True)
          n = self.caracteristica.count()
          par = False;
          if (n % 2 == 0):
              print("La cantidad de observaciones es par.")
              par = True
  
          if par:
              rango = (n / 2);
              print("RANGO = "+str(rango))
              rangoPython = rango-1
              valor1 = caracteristica[rangoPython]
              valor2 = caracteristica[rangoPython+1]
              mediana = valor1 +((valor2-valor1)/2)
          else:
              rango = ((n + 1) / 2)
              rangoPython = rango - 1
              mediana = caracteristica[rangoPython]
  
          return [mediana, rango]
  
      def calculoModa(self):
          moda = Counter(self.caracteristica)
          return moda
  
      def calculoVarianzaDesviacionTipica(self):
          n = self.caracteristica.count()
          mediaAritmetica = self.caracteristica.mean()
          varianza = 0
          c3 = 0
          for valorObservacion in self.caracteristica:
              x = valorObservacion
              moy = mediaAritmetica
              c1 = valorObservacion - mediaAritmetica
              c2 = c1 * c1
              c3 = c3 + c2
  
          varianza = c3 / (n - 1)
  
          desviacionTipica = sqrt(varianza)
  
          return ([varianza, desviacionTipica])
  
      def calculoDelosCuartiles(self,mediana,rangoMediana):
          sort_caracteristica = self.caracteristica.sort_values()
          sort_caracteristica = sort_caracteristica.reset_index(drop=True)
          q1 = 0
          q2 = mediana
          q3 = 0
  
          #Cálculo Q1
          restoDivision = rangoMediana%2
          if (restoDivision != 0):
              q1 = sort_caracteristica[((rangoMediana/2)+1)-1]
          else:
              valorMin = sort_caracteristica[((rangoMediana/2)-1)]
              valorMax = sort_caracteristica[(rangoMediana/2)]
              q1 = (valorMin + ((valorMax - valorMin) / 2) + valorMax) / 2
  
          # Cálculo Q3
          nbdatos = len(sort_caracteristica)+1
          nbDatosDesdeMediana = nbdatos - rangoMediana
          restoDivision = nbDatosDesdeMediana % 2
          if (restoDivision != 0):
              q3 = sort_caracteristica[(rangoMediana+ceil(nbDatosDesdeMediana/2))-1]
          else:
              valorMinQ3 = sort_caracteristica[(rangoMediana+(nbDatosDesdeMediana/2))-1]
              valorMaxQ3 = sort_caracteristica[(rangoMediana+(nbDatosDesdeMediana/2))]
              q3 = (valorMinQ3 + ((valorMaxQ3 - valorMinQ3) / 2) + valorMaxQ3) / 2
  
  
          return ([q1, q2, q3])
  
  
      def criterioDeTukey(self, primerCuartil, tercerCuartil):
  
          valoresAberrantesInferiores = []
          valoresAberrantesSuperiores = []
          caracteristica = self.caracteristica.sort_values()
          intercuartil = tercerCuartil - primerCuartil
          print("Inter-cuartil = "+str(intercuartil))
          limiteInferior = primerCuartil - (1.5 * intercuartil)
          limiteSuperior = tercerCuartil + (1.5 * intercuartil)
  
          for valorObservacion in caracteristica:
              if valorObservacion < limiteInferior:
                  valoresAberrantesInferiores.append(valorObservacion)
  
              if valorObservacion > limiteSuperior:
                  valoresAberrantesSuperiores.append(valorObservacion)
  
          valoresAberrantes = valoresAberrantesInferiores + valoresAberrantesSuperiores
  
          return (valoresAberrantes)
  
  
  
      def visualizacion(self,media,mediana,cuartil_1,cuartil_2,cuartil_3):
  
          plt.subplot(2, 2, 1)
          plt.hist(self.caracteristica)
          plt.title("Histograma y media")
          plt.axvline(media, color='red', linestyle='dashed', linewidth=1,label = str(media))
          plt.legend(loc='upper right')
  
          plt.subplot(2, 2, 2)
          plt.hist(self.caracteristica)
          plt.title("Histograma y mediana")
          plt.axvline(mediana, color='green', linestyle='dashed', linewidth=1,label = str(mediana))
          plt.legend(loc='upper right')
  
          plt.subplot(2, 2, 3)
          plt.hist(self.caracteristica)
          plt.title("Histograma y cuartiles")
          plt.axvline(cuartil_1, color='orange', linestyle='dashed', linewidth=1,label = "Q1: "+str(cuartil_1))
          plt.axvline(cuartil_2, color='orange', linestyle='dashed', linewidth=1,label = "Q2: "+str(cuartil_2))
          plt.axvline(cuartil_3, color='orange', linestyle='dashed', linewidth=1,label = "Q3: "+str(cuartil_3))
          plt.legend(loc='upper right')
  
          plt.subplot(2, 2, 4)
          plt.boxplot(self.caracteristica)
          plt.title("Diagrama de caja y bigotes")
          plt.show()
  
  
      def analisisCaracteristica(self):
  
          print("-----------------------------------------")
          print("      MEDIDA DE TENDENCIA CENTRAL        ")
          print("-----------------------------------------\n")
  
          print("-- CANTIDAD DE OBSERVACIONES --")
          # -Cantidad de observaciones
          n = self.caracteristica.count()
          print("Cantidad de observaciones = " + str(n))
  
          print ("\n-- MIN --")
          valoresOrdenados = self.caracteristica.sort_values()
          valoresOrdenados = valoresOrdenados.reset_index(drop=True)
          print("Valor mínimo: "+str(valoresOrdenados[0]))
  
          print ("\n-- MAX --")
          valoresOrdenados = self.caracteristica.sort_values()
          valoresOrdenados = valoresOrdenados.reset_index(drop=True)
          print("Valor máximo: " + str(valoresOrdenados[len(valoresOrdenados)-1]))
  
          # -Media artimética:
          print("\n-- MEDIA --")
          media = self.calculoMediaAritmetica()
          print("Media aritmética calculada = " + str(media))
          print("> Observaciones: Si todas las observaciones tuvieran el mismo valor (reparto equitativo), este sería " + str(media))
  
          # -Media aritmética:
          print("\n-- MEDIANA --")
          mediana = self.calculoMediana()
          print("Mediana calculada = " + str(mediana[0]))
          print("> Observaciones: El valor que se encuentra en el punto medio de las observaciones es:" + str(mediana[0]))
          print("El reparto es: " + str(mediana[1]) + " valores en cada lado de la mediana")
  
          # -Moda
          print("\n-- MODA --")
          moda = self.calculoModa()
          print(moda)
          print("> Observacions: La moda permite determinar los valores observados con más frecuencia")
  
  
          print("\n\n-----------------------------------------")
          print("      MEDIDA DE DISPERSION        ")
          print("-----------------------------------------\n")
          print("-- RANGO --")
          print ("Rango de la serie = "+str(valoresOrdenados[len(valoresOrdenados)-1]-valoresOrdenados[0]))
          varianzaDesviacionTipica = self.calculoVarianzaDesviacionTipica()
  
          print("\n-- VARIANZA --")
          print("Varianza calculada = " + str(varianzaDesviacionTipica[0]))
  
          print("\n-- DESVIACION TIPICA --")
          print("Desviación típica calculada = " + str(varianzaDesviacionTipica[1]))
          desviacionTipica = varianzaDesviacionTipica[1]
          print("68 % de los valores de las observaciones se sitúan entre " + str(media - desviacionTipica) + " y " + str(
              media + desviacionTipica))
          print("95 % de los valores de las observaciones se sitúan entre " + str(media - (desviacionTipica * 2)) + " y " + str(
              media + (desviacionTipica * 2)))
          print("99 % de los valores de las observaciones se sitúan entre " + str(media - (desviacionTipica * 3)) + " y " + str(
              media + (desviacionTipica * 3)))
  
          print("\n\n-----------------------------------------")
          print("      CUARTILES        ")
          print("-----------------------------------------\n")
          cuartiles = self.calculoDelosCuartiles(mediana[0],mediana[1])
          print("25 % de las observaciones tienen un valor inferior a " + str(cuartiles[0]))
          print("50 % de las observaciones tienen un valor inferior a " + str(cuartiles[1]))
          print("75 % de las observaciones tienen un valor inferior a " + str(cuartiles[2]))
  
  
          print("\n\n-----------------------------------------")
          print("      DETECCION VALORES ABERRANTES        ")
          print("-----------------------------------------\n")
          print("> Criterios de Tukey")
          valoresAberrantes = self.criterioDeTukey(cuartiles[0], cuartiles[2])
          print("Cantidad de valores aberrantes: " + str(len(valoresAberrantes)))
          print("Valores:" + str(valoresAberrantes))
  
  
          print("\n\n-----------------------------------------")
          print("      VISUALIZACION        ")
          print("-----------------------------------------\n")
          print("Generación de las gráficas...")
          self.visualizacion(media,mediana[0],cuartiles[0],cuartiles[1],cuartiles[2])