miércoles, 16 de noviembre de 2016

Iteradores y generadores


Iteradores


En Python existen diferentes estructuras de datos que pueden ser recorridas secuencialmente mediante el uso de bucles. Estos objetos llamados iteradores, básicamente, son secuencias, contenedores y ficheros de texto.

La declaración for/in se utiliza con frecuencia para recorrer los elementos de distintos tipos de iteradores: los caracteres de una cadena, los elementos de una lista o una tupla, las claves y/o valores de un diccionario e incluso las líneas de un archivo:

# Recorrer los caracteres de una cadena:

cadena = "Python"
for caracter in cadena:
    print(caracter)

# Recorrer los caracteres de la cadena anterior, en sentido inverso.
    
for caracter in cadena[::-1]:
    print(caracter)

# Recorrer los elementos de una lista

lista = ['una', 'lista', 'es', 'un', 'iterable']
for palabra in lista:
    print(palabra)
    
# Recorrer los elementos de la lista anterior, al revés

for palabra in lista[::-1]:
    print(palabra)

# Obtener índice y usar para recorrer todos los elementos de la lista

for indice in range(len(lista)):
    print (indice, lista[indice])

# Recorrer las claves de un diccionario

artistas = { 'Lorca' : 'Escritor', 'Goya' : 'Pintor'}
for clave, valor in artistas.items():
    print(clave,':',valor)
    
# Leer las líneas de un archivo de texto, una a una

for linea in open("datos.txt"):
    print(linea.rstrip())



La función iter()


La función iter() se suele emplear para mostrar cómo funciona en realidad un bucle implementado con for/in. Antes del inicio del bucle la función iter() retorna el objeto iterable con el método subyacente __iter__(). Una vez iniciado el bucle, el método __next__() permite avanzar, en cada ciclo, al siguiente elemento hasta alcanzar el último. Cuando el puntero se encuentra en el último elemento si se ejecuta nuevamente el método __next__() el programa produce la excepción StopIteration:

lista = [10, 100, 1000, 10000]
iterador = iter(lista)
try:
    while True:
        print(iterador.__next__())        

except StopIteration:
    print("Se ha alcanzado el final de la lista")


Implementando una clase para iterar cadenas


Los métodos __next__() y __iter__() permiten declarar clases para crear iteradores a medida.

# Declara una clase para recorrer los caracteres de una cadena de texto 
# desde el último al primer carácter

class Invertir:
     def __init__(self, cadena):
         self.cadena = cadena
         self.puntero = len(cadena)
     def __iter__(self):
         return(self) 
     def __next__(self):
         if self.puntero == 0:
             raise(StopIteration)
         self.puntero = self.puntero - 1
         return(self.cadena[self.puntero])

# Declara iterable y recorre caracteres

cadena_invertida = Invertir('Iterable')
iter(cadena_invertida)

for caracter in cadena_invertida:
     print(caracter, end=' ')

# Devuelven caracteres que restan por iterar (ninguno):

print(list(cadena_invertida.__iter__()))  # []



La función range()


Cuando se desea ejecutar un bucle un número de veces determinado se suele utilizar la función range() que genera un rango de valores numéricos iterables que no necesitan ser almacenados en una lista o tupla.

for elemento in range(1, 11):
    print(elemento, end=' ')  # 1 2 3 4 5 6 7 8 9 10 

for elemento in range(10, 0, -1):
    print(elemento, end=' ')  # 10 9 8 7 6 5 4 3 2 1    


Generadores


Los generadores son una forma sencilla y potente de iterador. Un generador es una función especial que produce secuencias completas de resultados en lugar de ofrecer un sólo valor. En apariencia es como una función típica pero en lugar de devolver los valores con return lo hace con la declaración yield. Hay que precisar que el término generador define tanto a la propia función como al resultado que produce.

Una característica importante de los generadores es que tanto las variables locales como el punto de inicio de la ejecución se guardan automáticamente entre las llamadas sucesivas que se hagan al generador, es decir, a diferencia de una función común, una nueva llamada a un generador no inicia la ejecución al principio de la función, sino que la reanuda inmediatamente después del punto donde se encuentre la última declaración yield (que es donde terminó la función en la última llamada).

# Declara generador

def gen_basico():
    yield "uno"   
    yield "dos"
    yield "tres"
   
for valor in gen_basico():
    print(valor)  # uno, dos, tres

# Crea objeto generador y muestra tipo de objeto

generador = gen_basico()
print(generador)  # 
print(type(generador))  # 

# Convierte a lista el objeto generador y muestra elementos

lista = list(generador)
print(lista)  # ['uno', 'dos', 'tres']
print(type(lista))  # 

El siguiente generador produce una sucesión de 10 valores numéricos a partir de un valor inicial. El valor final se obtiene sumando 10 al inicial y el bucle se ejecuta mientras el valor inicial es menor que el final. El ejemplo muestra como se almacenan los valores de las variables en cada ciclo y el punto donde se reanuda el bucle en cada llamada.

def gen_diez_numeros(inicio):
    fin = inicio + 10    
    while inicio < fin:
        inicio+=1
        yield inicio, fin

for inicio, fin in gen_diez_numeros(23):
    print(inicio, fin)

En un generador puede aparecer varias declaraciones yield o ésta puede aparecer dentro de un bucle. Si durante la ejecución del generador el intérprete Python se encuentra con un return producirá una excepción de tipo StopIteration.


Relacionado:


Ir al índice del tutorial de Python 

jueves, 20 de octubre de 2016

Cálculo estadístico


El módulo statistics agrupa un conjunto de funciones para cálculo estadístico.

Las funciones están organizadas en dos grupos: las dedicadas a calcular valores medios y promedios y otras que miden la dispersión de los datos alrededor de un valor central en una muestra o población de datos.

Una población en la ciencia Estadística es el conjunto de todos los elementos objeto de un estudio y una muestra es un subconjunto, extraído de la población, cuyo estudio sirve para inferir características de toda la población. Por otro lado, un individuo es cada uno de los elementos que forman una población o una muestra.

El módulo statistics se incluyó en la librería estándar a partir de Python 3.4.


Cálculo de promedios y de valores medios


Media: mean(data)


La función mean() devuelve la media aritmética de los datos de una muestra o población. Esta media se calcula sumando todos los valores y después el resultado de la suma se divide entre el número de elementos.

Si no se facilitan elementos la función producirá el error StatisticsError.

import statistics as stats

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.mean(edades))  # 31


Media armónica: harmonic_mean(data)


La función harmonic_mean() -nueva en Python 3.6- devuelve la media armónica (H) de los datos de una muestra o población. La media armónica se corresponde con el recíproco de la media aritmética de los inversos de los datos.

Dicho de otro modo, la media armónica de tres valores (x, y, z) será equivalente a 3/(1/x+1/y+1/z). Es un tipo de media apropiada cuando se trabaja con tasas o proporciones en cálculos financieros o que emplean velocidades y tiempos.

Si la expresión utilizada no tiene datos o cualquiera de ellos es menor que 0 la función producirá el error StatisticsError.

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.harmonic_mean(edades))  # 30.89


Mediana: median(data)


La función median() devuelve la mediana o valor medio de los datos. La mediana es el valor central de un grupo de números ordenados por tamaño. Si la cantidad de elementos es par, la mediana es el promedio de los dos números centrales.

La función no requiere que la secuencia de números esté ordenada. Si no hay elementos la función retorna el error StatisticsError.

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.median(edades))  # 30


Mediana baja: median_low(data)


La función median_low() devuelve la mediana baja o el valor central inferior de los datos. Este valor siempre será uno del conjunto de los datos. Cuando el número de elementos sea impar devolverá el valor central; y cuando sea par, el menor de los dos valores centrales.

Si no hay elementos la función devuelve el error StatisticsError

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.median_low(edades))  # 26


Mediana alta: median_high(data)


La función median_high() devuelve la mediana alta o el valor central superior de los datos y siempre será alguno del conjunto de datos. Cuando el número de elementos sea impar la función devolverá el valor central; y cuando sea par, el mayor de los dos valores centrales.

Si no hay elementos la función devuelve el error StatisticsError

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.median_high(edades))  # 34


Mediana agrupada: median_grouped(data, interval=1)


La función median_grouped() devuelve la mediana de los datos agrupados por su frecuencia. Opcionalmente, los datos pueden estar agrupados en intervalos.

La mediana es el valor del término medio que divide la distribución de los datos ordenados en dos partes iguales. Para calcular la posición de la mediana con datos agrupados en tablas de frecuencias se utiliza la siguiente ecuación:

Posición_de_mediana = (número_de_datos + 1) / 2

La mediana es el valor de la posición resultante; o si el número de elementos es par será la media de los dos valores centrales.

Ejemplo: lista con 20 números (no es necesario que esté ordenada)

datos = [1, 2, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6]

Los datos anteriores agrupados por frecuencia quedarían así:

1 - 1
2 - 3
3 - 2
4 - 4
5 - 8
6 - 2


Total de elementos = 20

(20 + 1) / 2 = 10.5, la mediana se encuentra entre el elemento que ocupa la posición 10 y 11, es decir, los valores 4 y 5.

Finalmente, para calcular la mediana agrupada se calcula la media de los valores anteriores: (4+5)/2 = 4.5

Mediana agrupada = 4.5

datos = [1, 2, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6]
print(stats.median_grouped(datos))  


Si los datos están agrupados en intervalos la mediana se encontrará en el intervalo de clase donde la frecuencia acumulada llega hasta la mitad de la suma de las frecuencias absolutas (número_de_datos / 2)

datos = [1, 2, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6]
print(stats.median_grouped(datos, interval=1)) # 4.5
print(stats.median_grouped(datos, interval=2)) # 4.0


Para conocer los cálculos que se realizan cuando se utilizan intervalos se recomienda examinar el código fuente de la función.

Si no hay elementos la función producirá el error StatisticsError


Moda: mode(data)


La función mode() devuelve el valor más frecuente de los datos.

Si no hay elementos la función produce el error StatisticsError

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.mode(edades))  # 26


Medidas de dispersión


Varianza (población): pvariance(data, mu=None)


La función pvariance() devuelve la varianza de una población de datos. Esta función es equivalente a variance() pero se aplica a una población en lugar de a una muestra.

La varianza es la media de las diferencias con la media elevadas al cuadrado.

En definitiva, la función pvariance() para obtener la varianza primero calcula la media de los elementos:

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
media = (22 + 23 + 26 + 26 + 26 + 34 + 34 + 38 + 40 + 41) / 10 = 31

A continuación, a cada número se le resta la media obtenida y se eleva al cuadrado. Después, se suman todos los resultados:

dif_cuadrado = (22-31)**2 + (23-31)**2 + (26-31)**2 + (26-31)**2 + (26-31)**2 + (34-31)**2 + (34-31)**2 + (38-31)**2 + (40-31)**2 + (41-31)**2 = 468

Finalmente, se calcula la media de todas las diferencias al cuadrado:

varianza = 468 / 10 = 46.8

La varianza mide en qué medida se dispersan los valores de una muestra alrededor de un valor central.

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.pvariance(edades))  # 46.8


El argumento mu es opcional y si se facilita debe ser la media de los datos de la población. En caso contrario, la función calculará este dato automáticamente.

Si no hay elementos o hay menos de dos valores la función producirá el error StatisticsError


Desviación estándar (población): pstdev(data, mu=None)


La función pstdev() devuelve la desviación estándar de una población de datos, que se corresponde con la raíz cuadrada de la varianza de dicha población. Esta función es equivalente a stdev() pero se aplica a una población en lugar de a una muestra.

La desviación estándar mide cuánto se separan los datos. Así, para cada valor se tiene una forma de saber si es muy grande, muy pequeño o normal. Permite conocer a qué distancia están los valores de la desviación estándar de la media.

El argumento mu es opcional y si se facilita debe ser la media de los datos de la población. En caso contrario, la función lo calculará automáticamente.

edades = [22, 23, 26, 26, 26, 34, 34, 38, 40, 41]
print(stats.pstdev(edades))  # 6.84


Varianza (muestra): variance(data, xbar=None)


La función variance() devuelve la varianza de una muestra de datos. La varianza mide en qué medida se dispersan los valores de una muestra alrededor de un valor central.

El argumento xbar es opcional y si se facilita debe ser la media de los datos. En caso contrario, la función calculará este dato automáticamente.

Para calcular la varianza de una población entera utilizar la función pvariance().

Si no hay elementos o hay menos de dos la función producirá el error StatisticsError

muestra_edades = [22, 23, 26, 26, 34, 38, 40]
print(stats.variance(muestra_edades))  # 54.14


Desviación estándar (muestra): stdev(data, xbar=None)


La función stdev() devuelve la desviación estándar de una muestra de datos, que se corresponde con la raíz cuadrada de la varianza de dicha muestra.

La desviación estándar mide cuánto se separan los datos. Así, para cada valor se tiene una forma de saber si es muy grande, muy pequeño o normal. Permite conocer a qué distancia están los valores de la desviación estándar de la media.

Para calcular la desviación estándar de una población entera utilizar la función pstdev().

El argumento xbar es opcional y si se facilita debe ser la media de los datos. En caso contrario, la función lo calculará automáticamente.

muestra_edades = [22, 23, 26, 26, 34, 38, 40]
print(stats.stdev(muestra_edades))  # 7.35



Ir al índice del tutorial de Python