<jupyter_start><jupyter_text>* Tutorial https://pandas.pydata.org/docs/getting_started/10min.html<jupyter_code>#Forma usual de importar pandas
#es utilizando el alias pd
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#Lectura de archivos CSV
#Algunos parámetros de interés:
#sep o delimiter
#names
#index_col
#prefix
#na_values
#na_filter
#nrows
#skiprows
help(pd.read_csv)

#EXISTE UNA FAMILIA DE FUNCIONES read_*
#POR EJEMPLO
#help(pd.read_excel)
#help(pd.read_sas)
#Abre archivo csv
usd = pd.read_csv('../datos/USD_MXN.csv')
print(type(usd))
print('-' * 50)
#El método head nos permite
#ver los primeros n renglones
print(usd.head(10))
#La estructura básica
#de un dataframe
#es un objeto del tipo
#Series
fechas = usd['Date']
print(type(fechas))
print('-' * 50)
#Es un string
print(type(fechas[0]))
#Es posible convertir una columna
#de fechas a objetos Timestamp
usd['Date'] = pd.to_datetime(usd['Date'], format = '%Y-%m-%d')
print(type(usd['Date'][0]))
print('-' * 50)

#Objetos Timedelta
delta = usd['Date'][1] - usd['Date'][0]
print(type(delta))
print('-' * 50)
print(delta.days)
#Es posible ordenar
#un dataframe de acuerdo
#a los valores de una o más variables
#inplace = True si es un dataframe muy grande (ahorra memoria)
#Por defaul ascending es True
usd_ord = usd.sort_values(by = 'Date', ascending = False, inplace = False)

#Observe la primera columna (Index)
#No pertenece a el dataframe
print(usd_ord.head(5))
print('-' * 50)
#El método tail nos permite
#ver los últimos n renglones
print(usd_ord.tail(5))
#Sería natural acceder a los elementos
#de la siguiente manera
#PERO EL RESULTADO NO ES LO ESPERADO
#ESTO TIENE QUE VER CON EL ELEMENTO
#LLAMADO INDEX
print(usd_ord['Date'][0])
print(usd_ord.index)
print('-' * 50)
#Traeme el elemento que corresponde
#al index 0 (en este caso, la última observación)
print(usd_ord['Date'][0])
print('-' * 50)
#Traeme el elemento que corresponde
#al index 1305 (en este caso, la primera observación)
print(usd_ord['Date'][1305])
#MI SUGERENCIA (TAL VEZ NO ES LA MEJOR SOLUCIÓN):
#Cuando se crea un dataframe
#a partir de otro y se modifica el
#orden de los renglones o se crea un subconjunto, se puede
#utilizar el método reset_index(drop = True)
#para evitar problemas con el index de un dataframe
usd_ord = usd.sort_values(by = 'Date', ascending = False)
print(usd_ord.head(5))
print('-' * 50)
usd_ord = usd_ord.reset_index(drop = True) 
print(usd_ord.head(5))
print('-' * 50)
print(usd_ord['Date'][0])<jupyter_output><empty_output><jupyter_text>Manipulando Series<jupyter_code>#Serie
adj_close = usd['Adj Close']
print(type(adj_close))
print('-' * 50)
print(adj_close.head(10))
#Al igual que los arreglos de numpy
#las operaciones (comúnes) sobre objetos Series
#vectorizan (entrada por entrada)
print(adj_close.head(10) + 1)
#Es posible acceder a los elementos de
#un objeto Series como si fueran
#np.arrays
print(adj_close[0:10])
print('-' * 50)
print(adj_close[[0, 1, 4]])
print('-' * 50)
sub_conjunto = adj_close[adj_close > 20]
#Observe lo molesto que puede llegar a ser el Index
print(sub_conjunto.head(10))
sub_conjunto = adj_close[adj_close > 20].reset_index(drop = True)
print('-' * 50)
print(sub_conjunto.head(10))
#Podemos filtrar utilizando
#varias condiciones lógicas
cond_1 = adj_close > 18
cond_2 = adj_close < 20

#El operador & compara bit a bit (bitwise)
#en este caso entrada por entrada
#de cond_1 y cond_2

print(adj_close[cond_1 & cond_2])
print('-' * 50)
#el operador ~ niega una condición bit a bit
#~True es False y ~False es True
print(adj_close[ ~(cond_1 & cond_2)])
#https://wiki.python.org/moin/BitwiseOperators<jupyter_output><empty_output><jupyter_text>EjercicioObtenga la fecha en que se observó el precio de cierre ajustado más grande**Sugerencia**```pythonadj_close = usd['Adj Close']fechas = usd['Date']help(adj_close.argmax())```<jupyter_code>adj_close = usd['Adj Close']
fechas = usd['Date']
indice_max = adj_close.argmax()
fecha_max = fechas[indice_max]
print(f'La fecha del máximo fue {fecha_max}')
print(f'El valor del máximo fue {adj_close[indice_max]}')<jupyter_output><empty_output><jupyter_text>EjercicioUtilizando la serie `adj_close` calcule los rendimientos (aritméticos) de los precios.$$r_{t} = \dfrac{P_{t}}{P_{t - 1}} - 1$$Localice la fecha que tuvo el rendimiento absoluto más grande.**No puede utilizar cíclos for****Sugerencia**Los objetos del tipo Serie tienen el método `abs` para calcular el valor absoluto de cada uno de los elementos.<jupyter_code>#Número de observaciones
fechas = usd['Date']
adj_close = usd['Adj Close']
n = len(adj_close)

#LO SIGUIENTE NO FUNCIONA POR EL INDEX
#YA QUE SE EMPAREJAN LOS INDEX DE CADA SUB SERIE
#r = adj_close[1:] / adj_close[0:(n - 1)] - 1

#Esto sí funciona
pt = adj_close[1:].reset_index(drop = True)
pt_1 = adj_close[0:(n - 1)].reset_index(drop = True)
r = pt / pt_1 - 1

indice_max = r.abs().argmax()
#Desde 1 ya que las fechas están ordenadas
#de más antigua a más reciente
#La primera fecha no tiene rendimiento
fecha_max = fechas[1:].reset_index(drop = True)[indice_max]
print(f'La fecha con el rendimiento absoluto máximo es {fecha_max}')
#Los objetos Series tiene un método plot para graficar los datos
adj_close.plot(title = 'USDMXN Adj Close', color = 'g')
#SUGERENCIA: Es mejor utilizar matplotlib ya que brinda más control y opciones
plt.plot(fechas, adj_close, 'g')
plt.title('USDMXN Adj Close')
plt.xlabel('Fecha')
plt.ylabel('Tipo de cambio')
plt.grid()
plt.show()<jupyter_output><empty_output><jupyter_text>Manipulando DataFrames<jupyter_code>print(usd.head(10))
#Como hemos visto, es posible acceder
#a los elementos utilizando el nombre
#de las columnas
#RECUERDE que open es una palabra reservada
#es por eso que utilizo p_open
p_open = usd['Open']
print(p_open.head())
#Podemos acceder a los elementos utilizando
#la notación punto
#SIEMPRE Y CUANDO EL NOMBRE NO LLEVE
#UN ESPACIO
p_open = usd.Open
print(p_open.head())
#Podemos acceder a un subconjunto de columas
open_low = usd[ ['Open', 'Low'] ]
print(open_low.head(10))
print('-' * 50)
print(type(open_low))
#Podemos borrar columnas
del usd['Volume']
print(usd.head())<jupyter_output><empty_output><jupyter_text>Utilizando un IndexPuede considerar un Index como el nombre de los renglones<jupyter_code>#La columna Date ahora es el Index
#parse_dates = True para convertir los strings a datetime
usd_index = pd.read_csv('../datos/USD_MXN.csv', index_col = 'Date', parse_dates = True)
print(usd_index.head())
print('-' * 50)
#La columna Date ya no pertenece
#a las columas!!!
print(usd_index.columns)
print('-' * 50)
print(usd_index.index)
#Ya que Date no es una columna
#esto arroja un error
usd_index['Date']
#Una manera más fácil
#de entender el código
close = usd_index['Close'] 
print(close['2015-06-12'])
#Podemos hacer un slicing utilizando
#un rango de fechas
#SE INCLUYE EL EXTREMO DERECHO
print(close['2015-06-12':'2020-06-12'])
#No importa si las fechas
#no se encuentran dentro del index
print(close['1990-06-12':'2100-06-12'])
#Si el Index es del tipo datetime
#plot lo toma como el eje X
close.plot(grid = True, title = 'USDMXN', color = 'g')<jupyter_output><empty_output><jupyter_text>`iloc` y `loc`https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html<jupyter_code>#Es posible acceder a los elementos
#utilizando localizaciones basadas en enteros
#iloc = integer location
print(usd.head(10))
print('-' * 50)
#No utilizamos reset_index(drop = True)
usd_ord = usd.sort_values(by = 'Date', ascending=False)
print(usd_ord.head(10))
print('-' * 50)

#OBSERVE LOS []
#Renglón 1 columna 0
print(usd.iloc[0, 0])
print('-' * 50)
#Renglón 1 columna 0
print(usd_ord.iloc[1, 0])
print('-' * 50)
#INDEX 1
print(usd_ord['Date'][1])
#ERROR
#Es necesario utilizar iloc
usd[0,1]
#Con loc podemos hacer slicing
#utilizando etiquetas (nombres de columnas y valores de algún Index)

#Columna Close
print(usd_index.loc[:, 'Close'])
print('-' * 50)
#Columnas Open y Close en un rango de fechas
print(usd_index.loc['2019-01-01':'2019-12-31',  ['Close','Open'] ])<jupyter_output><empty_output><jupyter_text>Modificando Series y DataFrames<jupyter_code>#Agregando nuevas columnas
#a un dataframe
usd_index['Mi Columna'] = 1
print(usd_index)
print('-' * 50)
usd_index['Var Dia'] = usd_index['Adj Close'] / usd_index['Open'] - 1
print(usd_index)
#Al igual que las listas
#las columnas que extraen de un dataframe
#Se pasan como referencia
#Esto se hace por cuestiones de optimizar la memoria
adj_close = usd['Adj Close']
print(id(adj_close) == id(usd['Adj Close']))
#Modificamos adj_close
adj_close[0] = 1
print(adj_close)
print('-' * 50)
print(usd['Adj Close'])<jupyter_output><empty_output><jupyter_text>Combinando dataframeshttps://www.datacamp.com/community/tutorials/joining-dataframes-pandas<jupyter_code>usd = pd.read_csv('../datos/USD_MXN.csv', index_col='Date', parse_dates=True)
eur = pd.read_csv('../datos/EUR_MXN.csv', index_col='Date', parse_dates=True)
gbp = pd.read_csv('../datos/GBP_MXN.csv', index_col='Date', parse_dates=True)

#La columa volumen no es necesaria
del usd['Volume']
del eur['Volume']
del gbp['Volume']
union_usd_eur = pd.merge(left = usd, right = eur, on = 'Date',
                         suffixes=('_usd', '_gbp'))
print(union_usd_eur)
#Si solo queremos ciertas columnas
union_sub= pd.merge(usd['Close'], eur[['Close', 'Open']],
                    on = 'Date', suffixes=('_usd', '_eur'))

#Observe que los sufijos
#sólo se usan para identificar columnas
#con nombres repetidos
print(union_sub)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13219840269020597, 'fraction_numerical': 0.020701975620008407, 'mean_word_length': 5.906386066763425, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 40, 'https://': 4, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '99748', 'license': 'MIT', 'n_tokens_mistral': 3902, 'n_tokens_neox': 3716, 'n_words': 1029, 'path': 'teaching/decisiones_de_negocio_basadas_abr_jun_2020/notebooks/lec10-pandas.ipynb', 'repo_name': 'davidrmh/davidrmh.github.io', 'size': '9516'}