import pandas as pd

"""
Explanation: Pandas: Series
La librería Pandas ofrece clases que permiten manejar datasets para operaciones de análisis de datos.
Para importar la librería en un programa se usa:
End of explanation
"""


# Crear una seríe indicando la producción por mes
produccion = pd.Series( [120,130,110,150,170,180,170,160,190,175,160,141], 
                       index=['ene','feb','mar','abr','may','jun','jul','ago','sep','oct','nov','dec']  )
print(produccion)

# Crear una serie a partir de un diccionario
dict = { 'a':6.4, 'b':7.1, 'c':8.8, 'd':9.2, 'e':6.4, 'f':5.6, 'g':3.2 }
pesos = pd.Series(dict)
print(pesos)

# Crear una serie utilizando un secuencia aleatoria distribuida normalmente
# Si no se indican los indices, se numeran automáticamente al instanciar la serie
import numpy as np
aleatorio = pd.Series( np.random.randn(10) )
print(aleatorio)

"""
Explanation: Pandas es una librería orientada a objetos y los objetos más importantes que incluye son las Series y los DataFrames.
Series
Una serie es una colección de valores con un índice asociado. El índice puede ser numérico, nominal, fecha y hora, etc. y permite referenciar los datos de la serie por su índice.
Los siguientes son algunos ejemplos de creación de objetos Serie
End of explanation
"""


# Número de elementos
pesos.size

# Forma (número de filas, columnas). En el caso de las series solo son filas
pesos.shape

# Tipo de datos que contiene la serie
pesos.dtype

# Los indices de la serie
pesos.index

# Los valores contenidos en la serie
pesos.values

## Acceso a los elementos de la serie

"""
Explanation: Atributos y Métodos de las Series
Los siguientes son los atributos más importantes
End of explanation
"""


pesos['a']

# o usando el metodo .get
pesos.get('a')

pesos[0]

pesos['b':'e']

pesos[1:5]

pesos[:'c']

pesos[:3]

pesos['d':]

pesos[3:]

"""
Explanation: Los elementos de una serie se acceden de forma similar a los de una lista. Se pueden usar los índices de la serie, o la posición de los elementos, por ejemplo:
End of explanation
"""


pesos[ ['b','e','c'] ]

pesos[ [1,4,2] ]

"""
Explanation: También se puede seleccionar un subconjunto de índices o posiciones particular
End of explanation
"""


pesos[ pesos>7 ]

"""
Explanation: Indexado lógico
La indexación lógica permite obtener los elementos de la serie que cumplen con una condición lógica
End of explanation
"""


'a' in pesos

'h' in pesos

'Sep' in produccion

# Asignar valor a un indice
pesos.at['a'] = 5.0
pesos[0]

# Esta vacia la serie
pesos.empty

# Crear una serie vacia
volumenes = pd.Series()
volumenes.empty

"""
Explanation: Se puede comprobar si un determinado indice está presente en la serie
End of explanation
"""


pesos.drop(['e','c'])

"""
Explanation: Eliminar elementos de una Series
Se pueden eliminar elementos por medio del método .drop e indicando los índices a eliminar
End of explanation
"""


# Hacer una copia de la Series
pesos2 = pesos.copy()
# Hacer nulos algunos de sus elementos
pesos2.at['b'] = None
pesos2

pesos2.dropna()

"""
Explanation: De particular interes son casos en los que la serie contiene elementos nulos (NaN). Todos los elementos nulos se pueden eliminar con el método .dropna
End of explanation
"""


# Comprobar si todos/algunos elementos de la serie cumplen una condición lógica
(pesos>5).all()

(pesos<5).any()

# Indice correspondiente al mayor/menor valor de la serie
pesos.argmax()

pesos.argmin()

# Convertir a matriz de numpy
x = pesos.as_matrix()
print(x)
print(type(x))

# Estadísticas descriptivas de la serie
pesos.describe()

# Seleccionar un rango de elementos al principio/final de la serie
print( pesos.head(3) )
print( pesos.tail(2) )

"""
Explanation: Métodos para comprobar una condición lógica sobre todos los elementos de una Series
End of explanation
"""


# Crear una nueva serie
medidas = pd.Series({ 'b':6.1, 'c':8.2, 'd':7.3, 'e':5.4, 'f':6.5, 'h':3.2 })
print("pesos\n", pesos)
print("\nmedidas\n",medidas)

pesos.add(medidas)

pesos+medidas

pesos-medidas

pesos*medidas

pesos/medidas

"""
Explanation: Operadores aritméticos
Todas las operaciones aritméticas entre series se hacen alineando los indices de las series. Las operaciones se pueden realizar utilizando los métodos .add .sub .mul .div .pow .dot .abs .mod o los correspondientes operadores sobrecargados. Indices no presentes se asumen NaN (Not a Number)
End of explanation
"""


medidas.at['a']=1.
pesos.size, medidas.size

pesos<medidas

pesos.eq(medidas)

pesos!=medidas

pesos.ge(medidas)

# Comprobar si alguno de los elementos es nulo
pesos.isnull()

"""
Explanation: Operadores relacionales
Se cuenta asi mismo con los métodos relacionales estándar .lt .le .gt .ge .eq .ne o sus correspondientes operadores sobrecargados < <= > >= == !=. Solo son aplicables a series de igual longitud (no es oblogatorio que coincidan los índices).
End of explanation
"""


pesos.count(), pesos.sum(), pesos.mean(), pesos.median()

"""
Explanation: También se cuenta con las operaciones estádisticas comunes: .count .sum .min .max .mean .var .std .median .mode .cov .corr .autocorr .sem
End of explanation
"""


pesos.sort_values()

pesos.sort_index(ascending=False)

"""
Explanation: Se puede ordernar la serie por valores o por indices, en orden creciente o decreciente
End of explanation
"""


# Salvar la serie en un archivo
pesos.to_csv('pesos.csv')

# Leer la serie de un archivo
copia = pd.read_csv('pesos.csv', squeeze=True, index_col=0, header=None)

copia

# Graficar la serie
import matplotlib.pyplot as plt
pesos.plot()
plt.show()

# Obtener su histograma
pesos.hist()
plt.show()

"""
Explanation: Operaciones de entrada/salida
End of explanation
"""


