# Escriba aquí su código
def max_de_dos(num1, num2):
  if num1==num2 :
    print ("los numeros son iguales") 
    return
  return num1 if num1>num2 else num2

max_de_dos(4,4)

"""
Explanation: <a href="https://colab.research.google.com/github/jdamaster/Alucinema/blob/master/s03_Introduccion_a_Pandas.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
<p><img alt="Colaboratory logo" height="140px" src="https://upload.wikimedia.org/wikipedia/commons/archive/f/fb/20161010213812%21Escudo-UdeA.svg" align="left" hspace="10px" vspace="0px"></p>

<h1> Diplomado de Análisis de datos y Machine Learning en Python</h1>

El presente diplomado hace parte del centro de Big Data de la facultad de ciencias exactas y naturales (FCEN) de la Universidad de Antioquia.
<p><a name="contents"></a></p>

Contenido Sesión 3

<a href="#taller">1. Taller</a><br>
<a href="#manipulacionDatos">2. Manipulación de datos</a><br>
<a href="#formateo">2.1 Formateo de datos temporales</a><br>
<a href="#procesamientoNulos">2.2 Procesamiento de datos nulos</a><br>
<a href="#indexacion">2.3. Indexación y localización</a><br>
<a href="#organizacionDF">2.4. Organización de dataframes</a><br>
<a href="#graficasPandas">2.5. Graficando desde pandas</a><br>
<a href="#operacionesColFil">2.6. Operaciones con columnas y filas</a><br>
<a href="#agrupacion"> 2.7. Agrupando dataframes</a><br>
<a href="#renormalizacion">2.8. Renormalización de datos</a><br>
<a href="#bineado"> 2.9. Bineado de datos</a><br>
<a href="#concom"> 2.10. Concatenación y combinación de dataframes</a><br>
<a href="#tallpan">3. Taller Pandas</a><br>

<p><a name="taller"></a></p>

1. Taller
Pregunta 1
Definir una función max_de_dos(), que tome dos números como argumentos y devuelva el mayor de ellos. Si los números son iguales que imprima en pantalla "Los números son iguales" y no regrese ningún valor.
End of explanation
"""


# Escriba aquí su código
numbers = [386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399, 162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67, 104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958,743, 527 ]
for item in numbers:
  if item!=237:
    print(item) 
  else:
    break


"""
Explanation: Para ver la solución haga double-click <b>aquí</b>.
<!--

def max_de_dos(a, b):

  if a>b:
    output=a
  elif b>a:
    output=b
  else:
    print('Los números son iguales')
    output=None
  return output


-->

Pregunta 2
Escriba un código de python que imprima todos los números pares de una lista dada y que deje de mostrar números cuando se encuentre el número 237
Lista de números de muestra:
numbers = [
386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399, 162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67, 104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958,743, 527 ]
End of explanation
"""


# Escriba aquí su código
for i in range(1,21)
  print("For iteration {}".format(i))

var1=1
while var1<=20:
  print("while iteration {}".format(var1))
  var1+=1

"""
Explanation: Para ver la solución haga double-click <b>aquí</b>.
<!--

numbers = [
386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399, 162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67, 104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958,743, 527 ]

listaFin=[]
for i in numbers:
  if (i%2==0):
      listaFin.append(i)
  if (i==237): 
      break
print(listaFin)

-->

Pregunta 3


Mostrar con un while los números del 1 al 20


Mostrar con un for los números del 1 al 20
End of explanation
"""


# Escriba aquí su código

"""
Explanation: Para ver la solución haga double-click <b>aquí</b>.
<!--

a=1
while a<=20: 
  print(a)
  a+=1

#####

for a in range(1,21):
  print(a)

-->

Pregunta 4
Juguemos al juego de adivinar el numero, generaremos un número entre 1 y 20.
Nuestro objetivo es adivinar el número. Si fallamos nos dirán si es mayor o menor que el número buscado. También poner el número de intentos requeridos.
End of explanation
"""


# Escriba aquí su código

"""
Explanation: Para ver la solución haga double-click <b>aquí</b>.
<!--

def adivina_numero():

  import numpy as np  
  numero_buscado = np.random.randint(1, 21)

  encontrado = False
  intentos = 0

  while not encontrado:

      numero_usuario = int(input("Introduce el número buscado: "))

      if numero_usuario > numero_buscado:
          print("El número que buscas es menor")
          intentos = intentos +1
      elif numero_usuario < numero_buscado:
          print("El numero que buscas es mayor")
          intentos = intentos +1
      else:
          encontrado = True
          print("Has acertado el número correcto es " , numero_usuario, " te ha llevado ", intentos," intentos")

-->

Pregunta 5
Defina una función llamada es_cadena_no_vacia() que evalúe si una cadena de caracteres es vacía o no y que retorne un variable booleana según sea el caso
End of explanation
"""


# Escriba aquí su código

"""
Explanation: Para ver la solución haga double-click <b>aquí</b>.
<!--


        def es_cadena_no_vacia(cadena=None):
          if cadena==None:
            salida=False
          else:
            salida=True
          return salida

-->

Pregunta 6
Defina una función llamada es_numero() que evalúa si la entrada es un número y que retorne una variable booleana según sea el caso
End of explanation
"""


# Escriba aquí su código

"""
Explanation: Para ver la solución haga double-click <b>aquí</b>.
<!--

def es_numero(num=None):
  if type(num)==int or type(num)==float:
    salida=True
  else:
    salida=False

  return salida

-->

Pregunta 7
Defina una clase llamada Hotel que contenga los atributos nombre, ubicación, puntaje obtenido por votación, y precio. Se tienen las siguientes restricciones:

El nombre y la ubicación deben ser cadenas no vacías. 
El puntaje debe ser un número (sin restricciones sobre su valor) 
El precio debe ser un número distinto de cero.

si no se cumple alguna de las condiciones, ejecute un error que muestre en pantalla el problema.
Pista: Use las funciónes es_cadena_no_vacia() y es_numero()
End of explanation
"""


import pandas as pd
import numpy as np

df=pd.read_csv("https://raw.githubusercontent.com/diplomado-bigdata-machinelearning-udea/Curso1/master/s03/dataVentas2009.csv") 

df.head()

df.columns

df.shape

df.describe()

df["Producto4"].unique()  

"""
Explanation: Para ver la solución haga double-click <b>aquí</b>.
<!--

class Hotel(object):
    """ Hotel: sus atributos son: nombre, ubicacion, puntaje y precio."""

    def __init__(self, nombre = '*', ubicacion = '*',
                 puntaje = 0, precio = float("inf")):
        """ nombre y ubicacion deben ser cadenas no vacías,
            puntaje y precio son números.
            Si el precio es 0 se reemplaza por infinito. """

        def es_cadena_no_vacia(cadena=None):
          if cadena==None:
            salida=False
          else:
            salida=True
          return salida

        def es_numero(num=None):
          if type(num)==int or type(num)==float:
            salida=True
          else:
            salida=False

          return salida

        if es_cadena_no_vacia (nombre):
            self.nombre = nombre
        else:
            raise TypeError ("El nombre debe ser una cadena no vacía")

        if es_cadena_no_vacia (ubicacion):
            self.ubicacion = ubicacion
        else:
            raise TypeError ("La ubicación debe ser una cadena no vacía")

        if es_numero(puntaje):
            self.puntaje = puntaje
        else:
            raise TypeError ("El puntaje debe ser un número")

        if es_numero(precio):
            if precio != 0:
                self.precio = precio
            else:
                self.precio = float("inf")
        else:
            raise TypeError("El precio debe ser un número")

    def __str__(self):
        """ Muestra el hotel según lo requerido. """
        return self.nombre + " de "+ self.ubicacion+ \
                " - Puntaje: "+ str(self.puntaje) + " - Precio: "+ \
                str(self.precio)+ " pesos."
-->

<p><a name="manipulacionDatos"></a></p>

2. Manipulación de datos
End of explanation
"""


pd.to_datetime(df.Fecha).head()

pd.to_datetime(df.Fecha, format='%d/%m/%Y').head()

df.Fecha=pd.to_datetime(df.Fecha)
df.set_index('Fecha', inplace=True)
df.head()

"""
Explanation: <p><a name="formateo"></a></p>

2.1. Formateo de datos temporales
La función to_datetime permite convertir los datos a un formato especial de tiempo
End of explanation
"""


df.isna().head() 

"""
Explanation: <p><a name="procesamientoNulos"></a></p>

2.2. Procesamiento de datos nulos
End of explanation
"""


# Solo visual
df.Producto4.fillna(0)

# Cambiando los datos
df.Producto4.fillna(df.Producto4.mean(),inplace=True) #Importante inplace

"""
Explanation: Es lo mismo que df.isnull, ver: https://datascience.stackexchange.com/questions/37878/difference-between-isna-and-isnull-in-pandas
Usar el comando fillna() para reemplazar los valores nulos con el valor deseado. Puede hacerse en una sola columna df.columna.fillna() o en todas al mismo tiempo df.fillna()
End of explanation
"""


df.iloc[100:110]

"""
Explanation: <p><a name="indexacion"></a></p>

2.3. Indexación y localización
Usando los comandos iloc y loc  en un dataframe, se puede acceder a los datos con base en las etiquetas y la ubicación respectivamente
End of explanation
"""


df[['Producto1','Producto4']].iloc[100:110]

df.iloc[:,1:3]

df.loc["2009-10-01":"2009-10-10"]

"""
Explanation: Indexación para un subconjunto de columnas
End of explanation
"""


df.iloc[109,0]=1000

df.loc['2009-04-19','Producto1']=999

"""
Explanation: Parar reasignar un valor de dataframe, simplemente se debe saber la localización, ya sea por indice o por etiqueta y usar el operador de asignación =
End of explanation
"""


df[['Producto1']].iloc[108:110]

"""
Explanation: Validemos la re-asignación de los valores
End of explanation
"""


df.sort_values(by="Producto1").head()

"""
Explanation: <p><a name="organizacionDF"></a></p>

2.4. Organización de dataframes
El método sort_values permite organizar los dataframes con base a los valores de las columnas o las filas
End of explanation
"""


df.Producto1.plot(figsize=(15,3))

df.plot(figsize=(15,3))

"""
Explanation: <p><a name="graficasPandas"></a></p>

2.5. Graficando desde pandas
Pandas permite realizar algunas gráficas directamente con el comando DataFrame.plot().
End of explanation
"""


df.Producto1.plot(kind='hist')

df.hist(column='Producto1')

"""
Explanation: Además de gráficos de línea, pandas tiene otro tipo de gráficos los cuales se puede usar con el argumento kind dentro del método DataFrame.plot(). Los otro gráficos disponibles son:

'bar' or 'barh' for bar plots
'hist' for histogram
'box' for boxplot
'kde' or 'density' for density plots
‘'area' for area plots
'scatter' for scatter plots
'hexbin' for hexagonal bin plots
'pie' for pie plots
End of explanation
"""


df['Producto3sqrt']=df.Producto3.apply(np.sqrt)

df.tail()

df.apply(np.sum, axis=0)

df.apply(np.sum, axis=0).plot(kind='pie')

"""
Explanation: <p><a name="operacionesColFil"></a></p>

2.6. Operaciones con columnas y filas
La función apply regresa algún valor después de aplicarle una función a cada columna/fila del dataframe.
End of explanation
"""


df["month"] = [i.month for i in df.index]
df.head()

df.groupby("month").max()

df.groupby("month")["Producto1"].count()

df2 = pd.DataFrame({"Person": ["John", "Myla", "Lewis", "John", "Myla", "Lewis", "John", "Myla"], "Age": [24, 55, 21, 24, 26, 43, 64, 55]})
df2.head(8)

# Agrupar por persona y aplicar "min" a "Age"
df2.groupby("Person").Age.agg("min")             
#df2.groupby("Person").Age.agg(["min"])


# Agrupar por persona y edad y aplicar "count" a "Age"
df2.groupby(["Person","Age"]).agg({"Age":"count"})

# frecuencia de las edades
df2['Age'].value_counts()
#df2["Age"].value_counts(Normlize=True)

"""
Explanation: <p><a name="agrupacion"></a></p>

2.7. Agrupando dataframes
El comando groupby permite agrupar y generar calculos y operaciónes sobre los valores agrupados.
End of explanation
"""


from sklearn import preprocessing

scaled=preprocessing.scale(df[['Producto4', 'Producto3sqrt']])

scaled

scaled.mean(axis=0)

scaled.std(axis=0)

"""
Explanation: <p><a name="renormalizacion"></a></p>

2.8. Normalización de datos
En algunos algoritmos de Machine Learning (ML) es importante tener los datos normalizados. Para ellos la función scale se encarga de normalizar los datos quedando con una media cercana a 0 y una desviación estandar de 1
End of explanation
"""


df.Producto1[:10]

"""
Explanation: <p><a name="bineado"></a></p>

2.9. Bineado de datos
La función cut nos permite separar los datos en bloques (bins)
End of explanation
"""


pd.cut(df.Producto1[:10], bins=3)

"""
Explanation: Se puede seleccionar el número de bins deseado y el programa obtiene la distribución óptima de los mismos
End of explanation
"""


pd.cut(df.Producto1[:10],bins=[0,100,200,300])

"""
Explanation: Se puede seleccionar los intervalos de los bins deseados
End of explanation
"""


pd.cut(df.Producto1[:10],bins=3, labels=['bajo','medio','alto'])

"""
Explanation: Se pueden poner etiquetas sobre los bins
End of explanation
"""


raw_data_1 = {
        'subject_id': ['1', '2', '3', '4', '5'],
        'first_name': ['Alex', 'Amy', 'Allen', 'Alice', 'Ayoung'], 
        'last_name': ['Anderson', 'Ackerman', 'Ali', 'Aoni', 'Atiches']}

raw_data_2 = {
        'subject_id': ['4', '5', '6', '7', '8'],
        'first_name': ['Billy', 'Brian', 'Bran', 'Bryce', 'Betty'], 
        'last_name': ['Bonder', 'Black', 'Balwner', 'Brice', 'Btisan']}

raw_data_3 = {
        'subject_id': ['1', '2', '3', '4', '5', '7', '8', '9', '10', '11'],
        'test_id': [51, 15, 15, 61, 16, 14, 15, 1, 61, 16]}

data1 = pd.DataFrame(raw_data_1, columns = ['subject_id', 'first_name', 'last_name'])
data2 = pd.DataFrame(raw_data_2, columns = ['subject_id', 'first_name', 'last_name'])
data3 = pd.DataFrame(raw_data_3, columns = ['subject_id','test_id'])

# Concatenar por filas
all_data = pd.concat([data1, data2])
all_data

# Concatenar por columnas
all_data_col = pd.concat([data1, data2], axis = 1)
all_data_col

# Combinar all_data y data3 a lo largo del valor de subject_id
pd.merge(all_data, data3, on='subject_id')

# Combinar solo los datos que tienen el mismo 'subject_id' en data1 y data2 (interseccion)
pd.merge(data1, data2, on='subject_id', how='inner')

# Combinar solo los datos que tienen el mismo 'subject_id' en data1 y data2 (union)
pd.merge(data1, data2, on='subject_id', how='outer')



"""
Explanation: <p><a name="concom"></a></p>

2.10. Concatenación y combinación de dataframes
La concatenación se realiza mediante la función concat() y la combinación mediante la función merge()
End of explanation
"""


