Conociendo Pandas. Parte 1.

Conociendo Pandas. Parte 1.

En este artículo conoceremos la librería Pandas, que es fundamental en Python para el análisis de datos, y veremos como extraer datos de un dataframe.

Recuerda unirte a nuestro canal en Telegram de Python Para Trading

Pandas nos hace la vida un poco más fácil.

Muchos estamos familiarizados con Excel y con sus hojas de cálculos pues son ampliamente utilizadas para almacenar y manipular datos, especialmente numéricos, y hacer cálculos.

En Python tenemos varias librerías para manipular todo tipo de datos, pero una es especialmente eficiente para las series temporales, Pandas.

Pandas tiene dos objetos básicos las series, que son vectores indexados y los dataframes, que son matrices n-dimensionales indexadas. Podemos pensar en un dataframe como una hoja de calculo de Excel supervitaminada.

Una serie temporal es una secuencia de datos tomados en determinados momentos ordenados cronológicamente. Por ejemplo la cotización de cierre de una acción, tenemos una sucesión de valores medidos al final de la sesión de cada día. En análisis financiero prácticamente tratamos con series temporales casí todo el tiempo, y Pandas nos hace la vida mucho mas fácil.

De hecho Pandas fue desarrollada inicialmente por Wes McKinney cuando trabajaba en un hedge found, AQR Capital Management, y posteriormente liberada como open source.

Esta librería tiene un gran número de funciones para realizar cálculos y manipulaciones de series y dataframes, y se ha originado un ecosistema al ser usada como base por otras librerías de terceros, que hacen uso de estos objetos y sus funciones.

Si has instalado Anaconda para usar Python, el módulo de Pandas ya viene preinstalado. Si no es así puedes instalarlo usando conda, basta con ejecutar en el terminal de comandos:

conda install pandas

o bien usando pip:

pip install pandas


Dataframes

Un DataFrame de Pandas es una matriz ordenada que puede contener cualquier tipo de datos. Se ordena en columnas, que deben contener siempre un mismo tipo de dato, y en filas que generan un indice.

Para practicar con los dataframes vamos a usar series temporales de cotizaciones diarias de acciones. Para lo cual nos descargaremos los datos históricos.

Pandas Datareader es un módulo asociado a Pandas que permite descargar datos desde distintas fuentes.

Sin embargo, debido a que Yahoo descontinuó su API para datos financieros, para poder descarga desde Yahoo Finance correctamente, utilizaremos el módulo Fix Yahoo Finance. Si no lo tienes instalado puedes instalarlo con :

pip install fix-yahoo-finance

Vamos a comenzar importando esos dos módulos. Importamos Pandas y para simplificar le asignamos el nombre pd, igual hacemos con Fix Yahoo Finance.

In [1]:
import pandas as pd
import fix_yahoo_finance as yf

Descargaremos el histórico diario de Santander y BBVA desde principios de 2008 hasta final de 2018, once años. Para ello pasaremos como parámetros los tickers de los valores y las fechas de inicio y fin. Los tickers para otros valores se pueden localizar en la página de Yahoo Finance.

En un futuro artículo hablaremos sobre las fuentes de datos financieros y sus API, pero de de momento nos bastará con saber como bajar los datos que necesitamos para estudiar los dataframe de Pandas.

In [3]:
start = '2008-01-01'
end = '2018-12-31'
tickers=['SAN.MC', 'BBVA.MC']
In [5]:
san = yf.download(tickers[0], start=start, end=end)
[*********************100%***********************]  1 of 1 downloaded
In [7]:
bbva = yf.download(tickers[1], start=start, end=end)
[*********************100%***********************]  1 of 1 downloaded

Lo que recibimos en la variable san es un DataFrame de Pandas, una matriz de dos dimensiones, como dijimos algo similar al un hoja de calculo de Excel.

Vamos a ver usando el método head() las cinco primeras lineas del dataframe san.

In [12]:
san.head()
Out[12]:
Open High Low Close Adj Close Volume
Date
2008-01-02 13.2489 13.3302 13.0953 13.1766 5.868722 103998100
2008-01-03 13.1405 13.2037 12.9689 13.0502 5.812426 113222703
2008-01-04 13.0050 13.0863 12.7070 12.8605 5.727935 100543802
2008-01-07 12.8154 12.9057 12.6889 12.7793 5.691768 71995342
2008-01-08 12.7883 12.8063 12.4993 12.5806 5.603270 83100401

Y con tail() las cinco últimas de bbva.

In [13]:
bbva.tail()
Out[13]:
Open High Low Close Adj Close Volume
Date
2018-12-21 4.6330 4.6600 4.5340 4.6185 4.484022 96489872
2018-12-24 4.5745 4.5990 4.5425 4.5580 4.425283 5652399
2018-12-27 4.6000 4.6380 4.4765 4.5450 4.412662 32726196
2018-12-28 4.5585 4.6480 4.5500 4.6410 4.505866 20924025
2018-12-31 4.6350 4.6635 4.6200 4.6355 4.500526 6885185

Vamos a usar la función de Pandas describe(), que nos dará un resumen estadístico de los datos contenidos en el dataframe. Lo que este método nos devuelve es un nuevo dataframe con los datos agregados.

In [9]:
san.describe()
Out[9]:
Open High Low Close Adj Close Volume
count 2810.000000 2810.000000 2810.000000 2810.000000 2810.000000 2.810000e+03
mean 6.646034 6.736354 6.539929 6.641825 4.465291 9.608371e+07
std 2.121196 2.148194 2.085775 2.120126 0.873586 7.419585e+07
min 3.117870 3.327370 3.098200 3.245730 1.878404 7.377082e+06
25% 5.216290 5.280958 5.134370 5.203502 3.782240 5.309241e+07
50% 6.019360 6.091000 5.920515 6.028705 4.549748 7.453013e+07
75% 7.740327 7.832793 7.634295 7.738860 5.212689 1.127219e+08
max 13.248900 13.330200 13.095300 13.176600 6.157157 9.092368e+08
In [10]:
bbva.describe()
Out[10]:
Open High Low Close Adj Close Volume
count 2810.000000 2810.000000 2810.000000 2810.000000 2810.000000 2.810000e+03
mean 7.973480 8.078099 7.849524 7.968902 5.647123 5.260945e+07
std 2.116239 2.133773 2.090441 2.113349 1.128175 4.675970e+07
min 4.400000 4.540000 4.275310 4.430000 2.550083 5.217450e+06
25% 6.381500 6.465500 6.287000 6.375000 4.757789 2.698997e+07
50% 7.609500 7.699500 7.472000 7.597500 5.642354 3.900200e+07
75% 9.042500 9.157500 8.911750 9.040450 6.657778 6.072695e+07
max 16.102100 16.159700 15.852300 15.929100 8.330698 6.009019e+08

Vemos que tanto en este caso, como al usar head() y tail() no hemos aplicado la función y pasado entre paréntesis el dataframe como un parámetros, sino que tras el nombre del dataframe con un punto pasamos la función. Esto es debido a que describe() es un método del objeto dataframe, y ya lo toma como parámetro. Es especialmente útil y permite el encadenado de métodos, es decir ir agregando funciones que toman el resultado de la anterior como parámetro.

Otro método que nos proporciona información resumida es info().

In [14]:
san.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2810 entries, 2008-01-02 to 2018-12-31
Data columns (total 6 columns):
Open         2810 non-null float64
High         2810 non-null float64
Low          2810 non-null float64
Close        2810 non-null float64
Adj Close    2810 non-null float64
Volume       2810 non-null int64
dtypes: float64(5), int64(1)
memory usage: 153.7 KB
In [15]:
bbva.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2810 entries, 2008-01-02 to 2018-12-31
Data columns (total 6 columns):
Open         2810 non-null float64
High         2810 non-null float64
Low          2810 non-null float64
Close        2810 non-null float64
Adj Close    2810 non-null float64
Volume       2810 non-null int64
dtypes: float64(5), int64(1)
memory usage: 153.7 KB

Revisamos las dimensiones de cada dataframe, vemos que tienen dos dimensiones, que reflejan las filas y las columnas.

2810 filas correspondientes a los días de cotización, y 6 columnas para los distintos datos.

Para ello usamos shape, que como vemos no lleva paréntesis detrás, esto es debido a que es una propiedad del dataframe y no un método del mismo.

In [17]:
san.shape
Out[17]:
(2810, 6)
In [18]:
bbva.shape
Out[18]:
(2810, 6)

Podemos extraer el índice y las columnas de un dataframe usando index y columns respectivamente.

El índice de nuestros dataframe son las fechas de los días cotizados.

In [83]:
san.index
Out[83]:
DatetimeIndex(['2008-01-02', '2008-01-03', '2008-01-04', '2008-01-07',
               '2008-01-08', '2008-01-09', '2008-01-10', '2008-01-11',
               '2008-01-14', '2008-01-15',
               ...
               '2018-12-14', '2018-12-17', '2018-12-18', '2018-12-19',
               '2018-12-20', '2018-12-21', '2018-12-24', '2018-12-27',
               '2018-12-28', '2018-12-31'],
              dtype='datetime64[ns]', name='Date', length=2810, freq=None)

Mientras que las columnas recogen los distintos valores de cada día cotizado.

In [13]:
san.columns
Out[13]:
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'], dtype='object')

Observamos como en ambos casos nos muestra 6 columnas y 5 filas. Las columna Open por ejemplo contiene todos los precios de apertura de cada acción. Y las filas están precedidas por una fecha que es el índice de nuestro dataframe. Así podemos obtener datos llamándolos por su columna, su índice o por una combinación de ambos.

Extracción de datos.

Puede ser que nos interese solo una columna y podemos extraerla del dataframe, lo que nos devuelve es un objeto Serie de Pandas.

In [24]:
san['Close']
Out[24]:
Date
2008-01-02    13.1766
2008-01-03    13.0502
2008-01-04    12.8605
2008-01-07    12.7793
2008-01-08    12.5806
2008-01-09    12.3277
2008-01-10    12.1832
2008-01-11    12.3819
2008-01-14    12.2103
2008-01-15    11.7407
2008-01-16    11.6052
2008-01-17    11.5149
2008-01-18    11.2349
2008-01-21    10.2234
2008-01-22    10.6930
2008-01-23    10.1782
2008-01-24    10.8466
2008-01-25    10.9188
2008-01-28    10.8285
2008-01-29    10.8917
2008-01-30    10.8466
2008-01-31    10.6840
2008-02-01    10.9007
2008-02-04    10.8285
2008-02-05    10.2505
2008-02-06    10.4763
2008-02-07    10.4763
2008-02-08    10.5034
2008-02-11    10.3227
2008-02-12    10.7111
               ...   
2018-11-16     4.2200
2018-11-19     4.2030
2018-11-20     4.0800
2018-11-21     4.1530
2018-11-22     4.0855
2018-11-23     4.0875
2018-11-26     4.2055
2018-11-27     4.1985
2018-11-28     4.2245
2018-11-29     4.2135
2018-11-30     4.1850
2018-12-03     4.2720
2018-12-04     4.1675
2018-12-05     4.1390
2018-12-06     3.9800
2018-12-07     3.9790
2018-12-10     3.8730
2018-12-11     3.8790
2018-12-12     4.0105
2018-12-13     4.0710
2018-12-14     4.0595
2018-12-17     4.0270
2018-12-18     3.9900
2018-12-19     4.0415
2018-12-20     3.9780
2018-12-21     3.9305
2018-12-24     3.8915
2018-12-27     3.8620
2018-12-28     3.9450
2018-12-31     3.9730
Name: Close, Length: 2810, dtype: float64

O bien solo nos interese una fecha en concreto.

In [27]:
bbva.loc['2018-12-31']
Out[27]:
Open         4.635000e+00
High         4.663500e+00
Low          4.620000e+00
Close        4.635500e+00
Adj Close    4.500526e+00
Volume       6.885185e+06
Name: 2018-12-31 00:00:00, dtype: float64

Vemos que para extraer una columna solo necesitamos pasar su nombre entre corchetes pero para un índice usamos el método loc. Pandas tiene diversas formas de extraer la información en función de como queramos hacerlo. Así el método loc extrae en base a la etiqueta del índice o columna, y el método iloc lo hace en función del numero ordinal que ocupa dicho índice o columna.

In [29]:
san.iloc[0]
Out[29]:
Open         1.324890e+01
High         1.333020e+01
Low          1.309530e+01
Close        1.317660e+01
Adj Close    5.868722e+00
Volume       1.039981e+08
Name: 2008-01-02 00:00:00, dtype: float64

Aunque para hacer una extracción por número de orden de varias filas no necesitamos usar iloc podemos hacerlo directamente.

In [57]:
san[100:106]
Out[57]:
Open High Low Close Adj Close Volume
Date
2008-05-26 12.0387 12.0387 11.8400 11.9122 5.491112 57912101
2008-05-27 11.9122 11.9935 11.7858 11.8129 5.445339 53285353
2008-05-28 11.8129 12.1019 11.8129 11.9664 5.516099 51351373
2008-05-29 11.9664 12.1200 11.8219 11.9664 5.516099 43426708
2008-05-30 11.9664 12.1200 11.9664 12.0929 5.574409 40694225
2008-06-02 12.0929 12.0929 11.7677 11.8219 5.449489 45788834

Si es útil cuando queremos hacer una extracción tanto de filas como de columnas.

In [31]:
san.iloc[:2,:2]
Out[31]:
Open High
Date
2008-01-02 13.2489 13.3302
2008-01-03 13.1405 13.2037

Ahora veamos el cierre ajustado y el volumen desde el 1 al 10 de noviembre de 2015.

In [35]:
san.loc['2015-11-01':'2015-11-10', 'Adj Close':'Volume'] 
Out[35]:
Adj Close Volume
Date
2015-11-02 4.374040 57910508
2015-11-03 4.416542 148301916
2015-11-04 4.483690 105920463
2015-11-05 4.425892 87484985
2015-11-06 4.509184 86906806
2015-11-09 4.413992 71676192
2015-11-10 4.398687 39123436

Si tenemos en cuenta que lo se devuelve siempre es un dataframe o una serie, podemos encadenar varias formas de hacer extracciones.

Así por ejemplo podemos tomar primero 3 columnas por su nombre y después las diez últimas filas. En este caso no necesitamos usar iloc.

In [33]:
san[['Close', 'Low', 'Volume']][-10:]
Out[33]:
Close Low Volume
Date
2018-12-14 4.0595 3.9755 65754616
2018-12-17 4.0270 4.0250 39823249
2018-12-18 3.9900 3.9900 44570803
2018-12-19 4.0415 4.0080 52444592
2018-12-20 3.9780 3.9465 58014374
2018-12-21 3.9305 3.8815 212350900
2018-12-24 3.8915 3.8745 15720836
2018-12-27 3.8620 3.8000 51788055
2018-12-28 3.9450 3.8630 35454221
2018-12-31 3.9730 3.9065 16730575

O tomar dos columnas y usar el método head(). Por ejemplo para tomar los 5 primeros mínimos y máximos

In [15]:
san[['High','Low']].head()
Out[15]:
High Low
Date
2008-01-02 13.3302 13.0953
2008-01-03 13.2037 12.9689
2008-01-04 13.0863 12.7070
2008-01-07 12.9057 12.6889
2008-01-08 12.8063 12.4993

Además loc cuando lo usamos sobre un índice de fechas (datetime) permite extraer usando periodos. Así si queremos extraer los datos de mayo de 2018 podemos hacer lo siguiente.

In [43]:
san.loc['2018-05']
Out[43]:
Open High Low Close Adj Close Volume
Date
2018-05-02 5.359 5.415 5.3570 5.3980 5.202323 320982382
2018-05-03 5.390 5.416 5.3150 5.3360 5.142571 260700006
2018-05-04 5.328 5.379 5.3010 5.3760 5.181121 34640717
2018-05-07 5.387 5.405 5.3560 5.3800 5.184977 94307179
2018-05-08 5.378 5.423 5.3250 5.3840 5.188831 68881042
2018-05-09 5.390 5.449 5.3850 5.4350 5.237983 64844626
2018-05-10 5.456 5.486 5.4120 5.4860 5.287134 37895987
2018-05-11 5.490 5.528 5.4740 5.5180 5.317974 45371092
2018-05-14 5.514 5.537 5.4760 5.5060 5.306409 32216377
2018-05-15 5.491 5.526 5.4320 5.4670 5.268823 39260950
2018-05-16 5.437 5.460 5.3120 5.3290 5.135825 54683384
2018-05-17 5.338 5.363 5.3070 5.3520 5.157991 43080699
2018-05-18 5.336 5.343 5.1880 5.2060 5.017283 90424536
2018-05-21 5.242 5.247 5.1560 5.1690 4.981625 29991460
2018-05-22 5.194 5.287 5.1660 5.2580 5.067399 35987383
2018-05-23 5.249 5.249 5.1250 5.1600 4.972951 44558462
2018-05-24 5.170 5.193 5.0960 5.1260 4.940184 41132419
2018-05-25 5.150 5.150 4.8965 4.9900 4.809114 91746984
2018-05-28 5.051 5.069 4.8700 4.8880 4.710812 54201863
2018-05-29 4.780 4.780 4.5570 4.6225 4.454936 149494992
2018-05-30 4.678 4.738 4.5740 4.6740 4.504569 106712823
2018-05-31 4.739 4.809 4.5385 4.6000 4.433251 110356744

Existe otro método que es mas específico como es xs, aunque es más utilizado para los dataframe multi índices. Los cuales dejaremos para mas adelante.

In [46]:
bbva.xs('2018-05', axis=0)
Out[46]:
Open High Low Close Adj Close Volume
Date
2018-05-02 6.718 6.782 6.718 6.765 6.443224 13089722
2018-05-03 6.760 6.760 6.638 6.666 6.348933 21502091
2018-05-04 6.680 6.752 6.630 6.742 6.421319 15853284
2018-05-07 6.770 6.812 6.753 6.788 6.465130 13997103
2018-05-08 6.791 6.817 6.730 6.756 6.434652 14733147
2018-05-09 6.768 6.810 6.735 6.806 6.482275 13014601
2018-05-10 6.820 6.852 6.789 6.852 6.526086 11695143
2018-05-11 6.852 6.885 6.838 6.854 6.527991 10440243
2018-05-14 6.845 6.886 6.796 6.833 6.507990 11957980
2018-05-15 6.828 6.855 6.669 6.729 6.408937 23286596
2018-05-16 6.708 6.743 6.558 6.620 6.305121 26357238
2018-05-17 6.640 6.708 6.610 6.707 6.387983 21490061
2018-05-18 6.700 6.706 6.546 6.580 6.267024 32373083
2018-05-21 6.610 6.623 6.489 6.508 6.198449 25198841
2018-05-22 6.526 6.649 6.513 6.615 6.300359 16624150
2018-05-23 6.572 6.572 6.392 6.467 6.159399 35696613
2018-05-24 6.484 6.524 6.347 6.386 6.082252 20716064
2018-05-25 6.408 6.416 6.124 6.208 5.912718 38816362
2018-05-28 6.307 6.338 6.090 6.134 5.842238 25479382
2018-05-29 6.070 6.087 5.850 5.878 5.598414 49828929
2018-05-30 5.950 6.022 5.825 5.916 5.634607 38820861
2018-05-31 5.981 6.066 5.783 5.839 5.561269 54235540

Pandas también permite hacer una extracción (slice) usando saltos, es decir tomando los valores de cada N filas. Por ejemplo podemos tomar cada 15 filas. En este caso después usamos tail() para limitar lo que se muestra a solo los 8 últimos seleccionados por facilitar la visualización.

In [50]:
san[::15].tail(8)
Out[50]:
Open High Low Close Adj Close Volume
Date
2018-07-27 4.7795 4.8520 4.7650 4.8180 4.643349 51335277
2018-08-17 4.3955 4.3955 4.3110 4.3395 4.239388 42357176
2018-09-07 4.2425 4.2550 4.1400 4.1780 4.081614 53997610
2018-09-28 4.4335 4.4345 4.2520 4.3355 4.235480 69807695
2018-10-19 4.1100 4.1685 4.0270 4.1250 4.063220 92057905
2018-11-09 4.2545 4.2690 4.1955 4.2390 4.175512 35140481
2018-11-30 4.2270 4.2300 4.1515 4.1850 4.122321 38399674
2018-12-21 3.9600 3.9955 3.8815 3.9305 3.871633 212350900

Esto puede ser utilizado incluso para invertir el orden del índice, usando un paso negativo.

In [53]:
bbva[::-1].head(6)
Out[53]:
Open High Low Close Adj Close Volume
Date
2018-12-31 4.6350 4.6635 4.6200 4.6355 4.500526 6885185
2018-12-28 4.5585 4.6480 4.5500 4.6410 4.505866 20924025
2018-12-27 4.6000 4.6380 4.4765 4.5450 4.412662 32726196
2018-12-24 4.5745 4.5990 4.5425 4.5580 4.425283 5652399
2018-12-21 4.6330 4.6600 4.5340 4.6185 4.484022 96489872
2018-12-20 4.6500 4.7150 4.6230 4.6540 4.518487 27462924

Indexación booleana

Otra forma de extraer datos es usando una condición y solo nos devolverá la filas que cumplan dicha condición. Así por ejemplo podemos extraer las filas en las que el cierre de Santander supero los 13 euros, comprobando que solo fueron dos.

In [61]:
san[san['Close']>13]
Out[61]:
Open High Low Close Adj Close Volume
Date
2008-01-02 13.2489 13.3302 13.0953 13.1766 5.868722 103998100
2008-01-03 13.1405 13.2037 12.9689 13.0502 5.812426 113222703

La condición puede ser tan compleja como queramos, pudiendo usar los operadores lógicos para encadenar distintos condicionales.

In [73]:
bbva[(bbva.Open>15) & (bbva.Volume>10**8)]
Out[73]:
Open High Low Close Adj Close Volume
Date
2008-01-04 15.7946 15.7946 15.3239 15.4680 8.089549 138136794
2008-01-09 15.2374 15.2374 14.9972 15.0452 7.868430 107909320

Vemos además como en el ejemplo anterior hemos llamado las columnas Open y Volume sin usar los corchetes ni las comillas. Y es que si el nombre de la columna es una sola palabra (no contiene espacios) podemos llamarla usando solo un punto, como si de una propiedad del objeto dataframe se tratara.

Esto mismo también es posible hacerlo usando el método query(). Obtendremos exactamente el mismo resultado, solo que usando una expresión contenida en una cadena de texto para realizar la búsqueda.

In [76]:
bbva.query('Open>15 and Volume>10**8')
Out[76]:
Open High Low Close Adj Close Volume
Date
2008-01-04 15.7946 15.7946 15.3239 15.4680 8.089549 138136794
2008-01-09 15.2374 15.2374 14.9972 15.0452 7.868430 107909320

Dado que los dataframes san y bbva tienen el mismo tamaño (de hecho el mismo índice por cotizar en el mismo mercado), podemos usar una condición aplicada Santander para extraer valores del BBVA. Así por ejemplo podemos ver como cotizaba BBVA cuando Santander superó los 13 euros en su precio de cierre.

In [82]:
bbva[san.Close>13]
Out[82]:
Open High Low Close Adj Close Volume
Date
2008-01-02 16.1021 16.1597 15.8523 15.9291 8.330698 98910228
2008-01-03 15.9291 15.9291 15.6986 15.7946 8.260358 90699092

Si quieres ampliar información la propia documentación oficial de Pandas explica muy detalladamente estos métodos de extracción y algún otro.

In [ ]:
 

Puedes probar y modificar el notebook usado en este artículo, descargándolo desde su enlace en Github.

Share:

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *