Analisis Datos
Analisis Datos
Esta obra está bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 4.0 Internacional
Cursos de Formación en Informática - CFI
Tabla de contenidos
2.6 Ejercicios
3.5 Ejemplo 1
3.6 Ejemplo 2
3.7 Ejercicios
5.5 Ejercicios
6.4 Ejercicios
Sección 1.1
Los contenidos que se abordan en este curso permiten introducir al alumno en la computación cientí ca
en Python. En particular se abordarán aquellas partes del lenguaje y aquellas librerías que resultan
necesarias para resolver el amplio conjunto de problemas relacionados con el cálculo numérico y el
análisis de datos. Los objetivos son:
En cada tema del curso se presentan una serie de conceptos junto con ejemplos prácticos relacionados
con los conceptos explicados. Al nal de cada tema encontrarás ejercicios para entregar y que permitirán
evaluar el uso correcto de los conceptos desarrollados a lo largo del curso. Estos ejercicios son
obligatorios y deberás enviarlos a través de la herramienta de entrega de ejercicios del campus.
¿A quien va dirigido?
Python resulta accesible a un público con un per l no tan tecnológico como nos podemos imaginar. Este
curso va dirigido a todos aquellos estudiantes y profesionales que suelan trabajar con grandes
cantidades de datos y que necesiten extraer información interesante de los mismos.
Es necesario tener unos conocimientos mínimos del lenguaje de programación Python. Es recomendable
haber realizado el curso CFI de Introducción a la programación en Python antes de realizar este curso.
Sección 1.2
Para poder ejecutar los ejemplos contenidos en este curso, es necesario tener accesible un intérprete de
Python 3.X y las librerías de cálculo cientí co Numpy, Pandas y Matplotlib. Lo más sencillo es usar
Anaconda, que proporciona una distribución libre de los paquetes anteriormente mencionados. Incluye
Python 3.X. y multitud de utilidades. Anaconda es una plataforma para análisis de datos en Python y
puede descargarse de su sitio web https://www.anaconda.com/products/individual.
A continuación explicamos paso a paso cómo realizar la instalación de Anaconda. El tutorial está hecho
para Windows, pero Anaconda está disponible también para Mac OS y Linux.
Anaconda
Desde Windows, Linux o Mac lo más sencillo es instalar una distribución de Python gratuita llamada
Anaconda.
Como hemos comentado anteriormente, Anaconda es una distribución de Python que recopila muchas
de las bibliotecas necesarias en el ámbito de la computación cientí ca. Esta distribución incluye multitud
de utilidades que nos facilitarán el trabajo y es ideal para empezar.
Para instalarla, sólo tienes que descargar la versión de Anaconda para tu sistema operativo desde su
página web. Se te abrirá una ventana emergente donde te pedirá tu dirección de email, pero si no quieres
darla, cierra dicha ventana y podrás descargarlo sin problema.
Paso 3: Haga clic en Siguiente. Lea los términos de la licencia y haga clic en "Acepto".
Paso 4: Seleccione una instalación para "Just Me" y haga clic en "Next".
Paso 5: Seleccione la carpeta de destino que propone el instalador y haga clic en el botón Siguiente.
Paso 5: Recomendamos no agregar Anaconda a la variable de entorno PATH, ya que esto puede interferir
con otro software. Seleccionar Anaconda como su Python predeterminado.
Paso 6: Una vez completada la instalación, verifíquela abriendo Anaconda Navigator, un programa que
se incluye con Anaconda: desde el menú Inicio de Windows, seleccione el acceso directo Anaconda
Navigator.
Si se abre Navigator, ha instalado Anaconda correctamente.
Si su sistema operativo es Mac OS, busque Anaconda Navigator entre sus aplicaciones.
Anaconda incluye varios entornos de desarrollo. Entre ellos tenemos Jupyter notebook y Spyder.
Jupyter notebook es una aplicación web que permite crear documentos (llamados notebooks de
Jupyter) que contienen código vivo, texto, fórmulas, guras, y medios audiovisuales. Esto facilita una
explicación más detallada y atractiva de los conceptos que se quieren describir. Estos documentos se
visualizan con un navegador (Explorer, Chrome, Firefox, ...) y pueden incluir cualquier elemento accesible
a una página web, además de permitir la ejecución de código escrito en el lenguaje de programación
Python.
Jupyter reúne en un mismo componente todas las herramientas cientí cas estándar de Python.
Veremos a lo largo de este curso cómo podemos usar este entorno, junto con Python, para realizar taréas
típicas en el contexto del análisis de datos: importación y exportación, manipulación y transformación,
visualización y mucho más.
Puesta en marcha de Jupyter Notebook
Una vez realizada la instalación de Anaconda en cualquiera de los sistemas operativos mencionados, la
ejecución de Jupyter Notebook se puede realizar a través de la aplicación Anaconda Navigator.
También podemos ejecutar Jupyter notebook directamente. Para ello será necesario seleccionar el
acceso directo a dicha aplicación desde el menú Inicio de Windows.
Si su sistema operativo es Mac OS, busque Jupyter Notebook entre sus aplicaciones.
Mi recomendación es crer una carpeta nueva donde guardaremos el trabajo desarrollado en este curso.
Dicha carpeta puede estar alojada en la carpeta Documentos del usuario.
En este caso, desde la aplicación jupyter notebook podremos selecionar dicha parpeta por lo que se
abrirá una ventana del navegador con un aspecto similar al siguiente:
En la siguiente Sección nos adentraremos en el manejo del entorno de desarrollo Jupyter notebook.
Sección 1.3
Jupyter notebook ofrece una herramienta de ejecución interactiva con la cual es posible dar órdenes
directamente al intérprete de Python y obtener una respuesta inmediata para cada una de ellas.
Jupyter Notebook es una aplicación web que permite crear documentos que contienen código y texto
(entre otros elementos). Estos documentos se visualizan con el navegador (Explorer, Chrome, Firefox, ...).
Son archivos con extensión .ipynb. En ellos podemos escribir código python ejecutable, texto, dibujar
grá cas y mucho más.
Un notebook de Jupyter es un chero que contiene un conjunto de celdas donde cada celda puede ser de
distintos tipos:
Como podemos ver en el ejemplo anterior, ambas celdas tienen el mismo número, indicando que la
segunda es el resultado de la primera. La primera celda In [ ] contiene código a ejecutar y la segunda
celda Out[ ] se genera automáticamente como resultado de la ejecución de la primera.
Los comentarios dentro de secciones de código se hace poniendo el símbolo de gato, #, al inicio de la
línea.
Raw NBConvert: Son celdas que permiten escribir fragmentos de código sin que sean ejecutados.
Para crear un nuevo notebook es necesario desplegar el botón New y posteriormente seleccionar Python
3.
El efecto es que se abrirá otra pestaña en el navegador con un documento nuevo y sin nombre. Mejor
dicho, Jupyter le da un nombre por defecto (untitled) y que nosotros debemos cambiar una vez creado.
Para cambiar el nombre es necesario pinchar sobre la palabra Untitled y aparecerá una nueva ventana
donde podremos escribir el nuevo nombre.
Todas las celdas son susceptibles de ser ejecutadas. La ejecución de una celda de código Python,
ejecutará el código escrito en la celda y producirá una salida, que será el resultado de la ejecución. La
ejecución de celdas de tipo Markdown, dará formato al texto.
Para ejecutar una celda tienes que posicionarte en la celda y posteriormente pulsar el botón cuyo icono
es un triángulo mirando a la derecha.
Para crear celdas nuevas pulsaremos el botón con el icono del signo +. En la siguiente imagen puedes ver
distintas celdas de texto. La primera celda es de código, la segunda y cuarta son celdas de texto sin
ejecutar, mientras que la tercera es una celda de texto ya ejecutada. Si haces doble click en una celda ya
ejecutada, pasa a ser una celda no ejecutada y podrás modi carla añadiendo o eliminando texto. Luego
podrás volver a ejecutarla.
Como puedes observar, el menú es bastante intuitivo. Navega un poco por él a ver qué descubres.
Incrustar Imágenes
Incrustar vídeos
from IPython.display import YouTubeVideo
YouTubeVideo('SLCuL-K39eQ') # Para probarlo ejecutar esta celda
Header 1 Header 2
−∞
𝑐= √‾𝑎‾‾‾‾‾
2
+ 𝑏‾2
b = [1,2,3]
b?
# Para probarlo ejecutar esta celda
b.append?
# Para probarlo ejecutar esta celda
Y si usamos dos símbolo ??, se muestra el código del método cuando sea posible.
También se puede combinar con el símbolo * para realizar búsquedas:
b.*p*?
# Para probarlo ejecutar esta celda
Mientras se escribe una expresión, si se pulsa el tabulador IPython ofrece ayuda para completar
la expresión.
El uso del tabulador también sirve para buscar los métodos de un objeto
También se usa para completar rutas de cheros.
b.remove?
b.append(2)
b
[1, 2, 3, 2]
Comandos mágicos
Son comandos especiales que permiten al usuario tener un control del comportamiento del
sistema.
Están precedidos por el símbolo %.
Ejemplos:
%reset : Limpia el espacio de trabajo actual y borra todos los nombres
de nidos por el usuario. Cuando traajamos con grandes volúmenes
de datos, Jupyter los mantiene en memoria incluso después de hacer
%del, por lo que no se libera memoria. En estos casos resulta útil.
b
%reset
## Para probarlo ejecutar esta celda
b
## Para probarlo ejecutar esta celda para ver qué ha ocurrido con la variable b
%timeit 3+4
%time 3+4
time 3+4
Comunicación con el SO
Es posible ejecutar comandos que se utilizan en las consolas de Windows o Linux.
%pwd
## Para probarlo ejecutar esta celda
Referencias
Esta práctica tiene como objetivo la toma de contacto con el sistema Jupyter, esto es, conocer el entorno
de programación en el que vamos a trabajar en este curso.
Supuesto que ya ha descargado e instalado Anaconda en la versión adecuada para su equipo, realize los
siguientes pasos:
Paso 1
Entrar en la carpeta Documentos de su equipo. Crear una nueva carpeta llamada pythonCFI (si no la
creó anteriormente).
Paso 2
Abrir la aplicación Anaconda Navigator y posteriormente lanzar Jupyter Notebook. Se abrirá una
pestaña nueva en su navegador por defecto. Se habrá abierto una pestaña en el navegador con nombre
Home. Pinchar cobre la carpeta Documentos. Luego muévase a la carpeta pythonCFI pinchando sobre
ella.
Paso 3
Crear un documento nuevo pulsando el botón 'New' y seleccionando 'Python 3'. Observar que se ha
abierto una nueva pestaña en el navegador.
Paso 4
Paso 5
Por defecto las celdas son de tipo code. Seleccionar la primera (y única) celda y cámbiele el tipo a
Markdown. Escribir en dicha celda el contenido del siguiente chero de texto. Ejecutar la celda y observar
el resultado.
Paso 6
Crear una nueva celda de tipo code. Escribir en ella la siguiente expresión:
8 * (5 + 4)
Paso 7
%pwd
Posteriormente, ejecutar la celda y observar el resultado. Se trata de un comando que permite conocer la
carpeta en el que se encuentra en este momento.
Paso 8
Pulsa el botón que permite grabar el documento. Posteriormente ve a la pestaña con nombre Home.
Observa que ahora hay un documento llamado documento_inicial.
Paso 9 y último
Ve al explorador de archivos, busca la carpeta pythonCFI. Compruebe que en esta carpeta se encuentra
el chero documento_inicial.ipynb.
En esta Sección hacemos un repaso de algunos elementos básicos del Python y que se presupone
conocidas para el estudiante. Si desear profundizar en los conceptos que se verán en esta sección,
recomendamos realizar el curso CFI de Introducción a la programación en Python.
Jupyter notebook ofrece una herramienta de ejecución interactiva con la cual es posible dar órdenes
directamente al intérprete y obtener una respuesta inmediata para cada una de ellas.
Python es un lenguaje interpretado. El intérprete actúa como una simple calculadora. Es posible
introducir una expresión y éste escribirá el resultado de evaluar la expresión.
La sintaxis es sencilla: los operadores +, -, * y / funcionan como en la mayoría de los lenguajes; los
paréntesis (()) pueden ser usados para agrupar expresiones.
# operación asrimética
2 + 4 - 5
10.3
print(total)
8
Las secuencias en Python
Tuplas
Una tupla es un tipo de datos que representa una secuencia de valores de cualquier tipo, a los cuales se
puede acceder mediante índices enteros. Las tuplas se caracterizan porque son inmutables.
Desde el punto de vista sintáctico, una tupla se de ne mediante una lista de valores separados por
comas y encerradas entre paréntesis.
# Ejemplos de Tuplas
tup1 = ( 8, 4 ) # colección de elementos (posiblemente de distintos tipos)
tup2 = ( 'Ana', 10, 'Alberto', 7 )
type(tup2)
tuple
tup1
(8, 4)
La tupla con nombre tup1 tiene dos elementos de tipo int, es decir de tipo entero.
La tupla con nombre tup2 tiene 4 elementos, dos de ellos son de tipo int, y los otros dos elementos son
de tipo str, es decir de tipo string.
Listas
Una lista es similar a una tupla con la diferencia fundamental de que puede ser modi cada una vez
creada. Se trata de un tipo de datos mutable y contiene datos heterogéneos.
Para crear una lista se utiliza la notación de corchetes. Se encierran los valores que forman la lista entre
corchetes.
# Ejemplos de listas
data1 = [2, 3, 4] # colección de elementos (posiblemente de distintos tipos)
data2 = ['Ana', 10, 'Alberto', 7, [ 7 ,8 ]]
data2
data1 + data2
[1, -9, 4, 1, 9, 0]
4 in data3
True
Una forma de crear secuencias de números enteros es mediante el uso de la función range. Esta función
genera una progresión de números enteros. La forma de crearla es mediante la llamada range(start,
end, step).
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 2, 4, 6, 8]
En el ejemplo anterior, creamos una tupla con nombre t. Dicha tupla contiene los elementos pares
comenzando con el valor 0 y terminando en el 9.
Los elementos de las secuencias pueden ser accedidos mediante el uso de corchetes [ ], como en otros
lenguajes de programación. Podemos indexar las secuencias utilizando la sintaxis:
[<inicio>:<final>:<salto>]
En Python, la indexación empieza por CERO. Veamos algunos ejemplos de cómo se utiliza la notación
inicio: fin: salto.
data4 = [10, 20, 30, 40, 50 ,60]
10
60
Como los objetos de tipo list son mutables, podemos modi car su estructura, por ejemplo cambiando
el elemento almacenado en cualquiera de sus posiciones:
Referencias
En Python la unidad de estructuración de los programas son las funciones. Una función consiste en un
conjunto de sentencias que realizan una determinada acción y a la que se le asocia un nombre. Una vez
de nida la función, se puede invocar su ejecución a lo largo del programa mediante su nombre. En
general, la función devolverá como resultado un valor de retorno (aunque no siempre tiene que ser así).
Las funciones tienen una primera línea que sirve para de nirlas, indicando su nombre y los
valores que la función necesita para realizar su tarea. Dichos valores, llamados argumentos se
encuentran dentro de los paréntesis. La de nición de la función siempre termina con dos
puntos. Se declaran con la palabra reservada def.
Las funciones pueden devolver valores usando la palabra reservada return.
Los argumentos pueden ser de varios tipos ( posicionales, por clave y argumentos agrupados).
Aquí solo veremos los dos primeros tipos de argumentos.
Podemos utilizar la función anterior para sumar los números 4, 2 y 3. Para ello basta con escribir el
nombre la función junto con los valores 4, 2 y 3 como argumentos. A esto se le conoce como llamada a la
función o invocar a la función:
suma_tres(1,2,8)
11
a = 1
b = 8
# los valores de a y b se sustituyen por 1 y 8 respectivamente en la llamada
resultado = suma_tres (a,2,b)
resultado
11
A las funciones se les puede añadir comentarios, para lo cual basta incluir una cadena entre la línea
dónde se de ne def y el cuerpo usando una tripleta de comillas """.
def suma(x,y):
"""
Returns la suma de los dos parámetros x e y
Parameters
----------
x, y: int, float
A number
Returns
-------
int, float
Suma de los dos parámetros x e y
"""
return x + y
help(suma)
suma(x, y)
Returns la suma de los dos parámetros x e y
Parameters
----------
x, y: int, float
A number
Returns
-------
int, float
Suma de los dos parámetros x e y
El paso de argumentos a una función se hace por referencia, de manera que las modi caciones que se
hagan sobre los argumentos se mantienen después de la llamada y ejecución de la función:
a = [1, 2, 4]
addElement(a, 9)
a
[1, 2, 4, 9]
Los argumentos por clave se usan para indicar valores por defecto y siempre se sitúan después de los
argumentos posicionales. De esta forma podemos crear una función que puede ser invocada con menos
argumentos de los que permite.
Esta función puede ser invocada pasando sólo los argumentos obligatorios x e y :
Esta función puede ser invocada pasando los argumentos obligatorios x e y junto con un argumento por
clave:
Esta función puede ser invocada pasando los argumentos obligatorios x e y junto con los dos argumento
por clave:
15
Una función puede tener varias instrucciones return. Si la ejecución de una función no alcanza ninguna
instrucción return, se devuelve None que es el valor que representa el valor nulo en Python. El tipo de
None es NoneType.
r = suma_todos(2, 3, 4, 6)
r # r es None
type(r)
NoneType
La ejecución de una función termina en el momento en el cual se alcance una instrucción return.
# función que pretende escribir cuatro cadenas: Uno, Dos, Tres, Cuatro
# si añadimos una instrucción return, no se ejecutan todas las instrucciones
def cuenta():
print('Uno')
print('Dos')
return ('Fin de la función') # la función termina tras ejecutar el return
print('Tres')
print('Cuatro')
cuenta()
Uno
Dos
'Fin de la función'
Las funciones Python pueden devolver múltiples valores. Aquí tenemos otra de las características que
hace a Python atractivo a los programadores.
def orden(a,b):
if a <= b:
return a, b
else:
return b, a
m = orden(7, 1)
m
(1, 7)
Semántica de Python
A diferencia de otros lenguajes como C++, Java o Perl, Python no utilliza corchetes o llaves para
estructurar el código. Python utiliza espacios en blanco o tabulaciones para dar estructura a su código.
def hola():
print('Hola Mundo!') # Tabulación al comienzo de ésta linea
Hola Mundo!
def producto(a,b):
res = a * b
print(res) # más o menos espacios al comienzo produce un error de sintaxis
return res
El uso de tabuladores hace el código más legible. En otros lenguajes el código de la función 'hola' ha de
escribirse utilizando llaves, como se muestra a continuación:
def hola():
{
print('Hola Mundo!')
}
Referencias
En Python, un diccionario es una colección no ordenada de pares clave - valor donde la clave y el
valor son objetos Python.
Características:
Crear un diccionario
De la misma forma que con listas, es posible de nir un diccionario directamente con los miembros que va
a contener, o bien inicializar el diccionario vacío y luego agregar los valores.
{}
cadenas,
enteros,
tuplas (con valores inmutables en sus miembros), etc.
Los valores de un diccionario pueden ser de cualquier tipo: listas, cadenas, tuplas, otros diccionarios,
objetos, etc.
Otra forma de crear un diccionario es declararlo vacío y luego insertar los valores. Se declara el
diccionario vacío, y luego se asignan valores directamente a las claves.
d2 = { }
d2['Juan'] = 609922565
d2['Ana'] = 691252580
d2['Luis'] = 642589569
d2
Si la clave existe en el diccionario, la operación de asignación modi ca su valor; si no exite dicha clave,
entonces se crea con el valor indicado en la asignación.
Para acceder al valor asociado a una determinada clave, utilizamos la notación de corchetes [ ] al igual
que hacíamos con las variables de tipo lista. En el caso de los diccionarios escribimos la clave elegida en
lugar del índice.
d2
d2 ['Luis']
Las claves son únicas dentro de un diccionario, es decir que no puede haber un diccionario que tenga dos
veces la misma clave. Si se asigna un valor a una clave ya existente, se reemplaza el valor anterior.
d2 ['Luis'] = 0
d2 ['Ana'] = ['938941523', '609585962']
d2
d2 ['Carlota']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-12-c0fadf735c7d> in <module>
----> 1 d2 ['Carlota']
KeyError: 'Carlota'
Para evitar este tipo de errores, podemos usar la función in, que comprueba si un elemento está en el
diccionario o utilizar el método get(), que devuelve el valor None si la clave no está en el diccionario.
'Carlota' in d2
False
'Luis' in d2
True
m = d2.get('Carlota')
type(m)
NoneType
Para eliminar elementos de un diccionario se utiliza el método pop() con la clave que se desea eliminar:
d2
e = d2.pop('Ana')
e
['938941523', '609585962']
d2
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-19-25795e3e7ecd> in <module>
----> 1 d2.pop('Carlota')
2 d2
KeyError: 'Carlota'
El método keys() devuelve una lista con las claves del diccionario. El método values() devuelve una
lista con los valores del diccionario.
d2
d2.keys()
dict_keys(['Juan', 'Luis'])
d2.values()
dict_values([609922565, 0])
Los diccionarios son secuencias de elementos, por lo que se les considera objetos iterables. Para recorrer
dichos elementos se pueden usar la sentencia for. Por ejemplo podemos recorrer las claves del
diccionario y a partir de ellas acceder a los valores.
Ana : 691252580
Juan : 609922565
Luis : 642589569
También es posible usar una sentencia for para recorrer los pares clave-valor que devuelve el
método items().
Ana -- 691252580
Juan -- 609922565
Luis -- 642589569
Ejemplos
Ejemplo 1
El siguiente diccionario
Solución: Cada cliente se corresponde con un elemento del diccionario. Basta con preguntar por el
número de elementos del diccionario para conocer el número de clientes:
len(agenda)
(b) Escribir las instrucciones necesarias para recuperar la lista de nombres de los clientes.
Solución: Las claves del diccionario representan los nombres de los clientes. Bastará con preguntar por
las claves para obtener los nombres de los clientes. Usatemos la función keys() de los diccionarios:
clientes = agenda.keys()
clientes
(c) Escribir las instrucciones necesarias para recuperar todos los teléfonos de Pedro.
Solución: Preguntaremos por el valor asociado a la clave Pedro. Usaremos el operador de acceso
corchete:
agenda = {'Ana': {'casa':91258471, 'movil': 9057963333},
'Pedro': {'movil':900111111, 'trabajo':609235236},
'Luis': {'casa': 458235236}}
telefonos_pedro = agenda['Pedro']
telefonos_pedro
(d) Escribir las instrucciones necesarias para recuperar el teléfono del trabajo de Pedro.
Solución: Recuperaremos todos los teléfonos de Pedro, y posteriormente, buscaremos el valor de la clave
trabajo:
telefonos_pedro = agenda['Pedro']
trabajo_de_pedro = telefonos_pedro['trabajo']
trabajo_de_pedro
609235236
(d) Escribir las instrucciones necesarias para modi car el teléfono del trabajo de Pedro. El nuevo teléfono
es 6000.
Solución: Recuperaremos todos los teléfonos de Pedro, y posteriormente, modi caremos el valor de la
clave trabajo:
telefonos_pedro = agenda['Pedro']
telefonos_pedro['trabajo'] = 6000
Podemos comprobar que la agenda se ha modi cado y el teléfono del trabajo de Pedro ha cambiado:
agenda
Referencias
El uso de funciones como argumentos de otras funciones es una característica de los lenguajes
funcionales.
Para mostar un ejemplo, de nimos en primer lugar una función que calcula el factorial de un número.
def factorial(num):
resultado = 1
for i in range(1, num + 1 ):
resultado = resultado * i
return resultado
Podemos probar la función anterior con el número 3. Así, el resultado devuelto por la función será 6.
factorial(3)
Supongamos ahora que queremos calcular el factorial de una lista de números. Una opción es crear una
segunda función con dos argumentos (la vamos a llamar aplicar_operacion). El primer argumento es
una función f y el segundo argumento es una lista m. La idea es que la función aplicar_operacion
aplique la función f a cada uno de los elementos de la lista m.
Veamos:
Ahora, podemos calcular el factorial de la lista de números [3,4,2,5] ejecutando el siguiente código:
aplicar_operacion(factorial, [3,4,2,5])
Como podemos ver, la función factorial se ejecuta sobre cada uno de los elementos de la lista [3, 4,
2, 5].
Los bene cios que me proporciona la función aplicar_operacion es que su primer parámetro puede
ser cualquier función. Por ejemplo, podemos calcular el valor absoluto de cada uno de los elementos de
una lista invocando a la función aplicar_operacion con el parámetro abs.
[3, 4, 2, 5]
La función abs es una función de Python que calcula el valor absoluto de un número.
La función map de Python se comporta de forma similar a la función aplicar_operacion. Aplica una
función a una colección de objetos:
En el siguiente ejemplo, la función map aplica la función abs a cada uno de los elementos de la lista de su
segundo parámetro:
[2, 5, 58]
En el siguiente ejemplo, se calcula el factorial de cada uno de los elementos de la lista en el segundo
argumento.
Las funciones anónimas son aquellas que no tienen nombre y se re eren a una única instrucción. Se
declaran con la palabra reservada lambda.
Su sintaxis es:
lambda e : expresión
Son funciones cortas. Están sintácticamente restringidas a una sola expresión y permiten escribir
funciones de forma más sencilla.
Las funciones Lambda pueden ser usadas en cualquier lugar donde sea requerido un objeto de tipo
función.
Supongamos que queremos calcular el doble de un número. La función siguiente realiza esa operación:
# función normal
def doble(a):
"""
Calcula el doble de un número
"""
return a * 2
lambda x: x * 2
<function __main__.<lambda>(x)>
[2, 4, 6, 8]
[2, 4, 6, 8]
Las funciones lambda se utilizan mucho en análisis de datos ya que es muy usual transformar datos
mediante funciones que tienen a otras funciones en sus argumentos. Se usan funciones lambda en lugar
de escribir funciones normales para hacer el código más claro y más corto.
Ejemplos
Ejemplo 1:
Utilizar la función map junto con una función lambda para construir una lista que solo tenga la letra de
cada uno de los NIF's. El resultado será:
En este caso, cada elemento de la lista m es de tipo string. El acceso a cada uno de los caracteres que
forman un nif, se realiza usando el operador de corchete junto con el índice al que queremos acceder.
Por ejemplo, para acceder a la primera posición, usaremos el índice 0.
e = '12346678N'
e[0]
'1'
De esta forma, con índices negativos podemos acceder a las posiciones en sentido inverso, es decir, de
derecha a izquierda. Así, la última posición está en el índice -1.
e = '12346678N'
e[-1]
'N'
La función lamda lambda x: x[-1] toma como entrada el parámetro x (que será de tipo string), y
devuelve la última posición de dicho string.
Ejemplo 2
Dada una lista de direcciones de correo electrónico, se desea extraer el nombre de los usuarios.
Para resolver este problema, tenemos que usar la fnción split de los strings, que permite dividir un
string en partes, devolviendo una lista de cadenas que componen el string. Ejecuta la siguiente celda para
ver la ayuda de dicha función
str.split?
Por ejemplo, si queremos partir el string nombre por el espacio en blanco, escribiremos:
En el caso de los emails, será buena idea partir el string por el símbolo @:
datos = '[email protected]'
datos.split('@')
['gonzalo', 'ucm.es']
Si observamos el resultado, podemos ver que el nombre del ususario de correo se encuentra en el índice
0 (primera posición de la lista devuelta por la función split.
Con todo lo anterior, ya podemos resolver el problema. Extraer el nombre de los usuarios a partir de una
lista de direcciones de correo:
Referencias
Ejercicios
Ejercicio 1
(a) Utiliza la función range para generar la lista de numeros impares con inicio 5 y n 101. Ambos
inclusive.
(b) Utiliza la función range para generar la tupla de numeros múltiplos de 10 con inicio 100 y n 10.
Ambos inclusive. El resultado será: (100, 90, ... , 10).
# Sol a)
# Sol b)
Ejercicio 2
ciudades = {
'Torremocha de Jarama': {'población': 942,
'superficie': 18.49,
'altitud': 707},
'Torres de la Alameda': {'población': 7825,
'superficie': 43.79,
'altitud': 658},
'Tres Cantos': {'población': 46,
'superficie': 37.93,
'altitud': 710},
'Valdaracete': {'población': 643,
'superficie': 64.31,
'altitud':733},
'Valdeavero': {'población': 1450,
'superficie': 18.79,
'altitud': 717},
'Valdelaguna': {'población': 867,
'superficie': 42.13,
'altitud': 700},
'Valdemanco': {'población': 916,
'superficie': 17,
'altitud': 1125}
}
(a) Escribe las instrucciones necesarias para recuperar la lista de todas las ciudades.
# Sol:
# Sol:
# Sol:
Ejercicio 3
se desea extraer el nombre del dominio. Por ejemplo, a partir de la lista anterior, queremos obtener la
lista:
Para resolver este problema, tenemos que usar la fnción split de los strings, que permite dividir un string
en partes.
# Sol:
Ejercicio 4
(a) Construye una función llamada calcular_iva que reciba como parámetro de entrada un valor
numérico que representa un importe. La función devuelve el 21% de IVA de dicho importe.
(b) Dada una lista de importes, queremos calcular el 21% de IVA de cada importe. Usa la función
aplicar_operacion de nida en la sección anterior para aplicar la función calcular_iva de nida en el
apartado anterior, a una lista de importes.
Ejemplos:
# Sol:
Sección 3.1
Importar módulos
Los módulos son programas que amplían las funciones y clases de Python para realizar tareas
especí cas.
Lo habitual cuando se desarrollan aplicaciones es que los programas se vuelvan muy largos. En estos
casos conviene organizar el código en distintos archivos dependiendo de su funcionalidad. Con esto
conseguimos:
Módulos estándar
Python viene con una biblioteca de módulos estándar. Algunos módulos se integran en el intérprete. Los
módulos integrados proporcionan operaciones que no son parte del núcleo del lenguaje (por ejemplo
primitivas del sistema operativo).
Para listar los módulos que tenemos instalados y que por tanto podemos importar en nuestros
programas, basta con ejecutar el siguiente comando:
!conda list
Si queremos usar un módulo que no está en la lista, tendremos que instalarlo primero. Para ello
podemos usar el comando conda install desde una ventana de comandos. Por ejemplo, para instalar
el módulo pymysql escribiremos:
conda install pymysql
Los módulos que tenemos disponibles se pueden importar de las siguientes formas:
array([2, 3, 4])
import <modulo>
array([2, 3, 4])
Si importamos todo el módulo con import numpy, para usar las funciones de nidas dentro del módulo
debemos poner el nombre del módulo delante del nombre de la función. Por ejemplo, para usar la
función array, es necesario escribir numpy.array.
Por último, Python permite referirnos a un módulo a través de un nombre corto o alias. Si queremos
importar el módulo completo con alias , usaremos la notación:
import numpy as np
a = np.array( [2,3,4] ) # se accede con el alias a la función array
a
array([2, 3, 4])
Para usar las funciones de nidas dentro del módulo, debemos poner el alias del módulo delante del
nombre de la función. Por ejemplo, para usar la función array, es necesario escribir np.array.
ó
Operaciones disponibles en un módulo
La función integrada dir() se usa para encontrar qué nombres de ne un módulo (constantes,
funciones, etc.). Devuelve una lista ordenada de cadenas:
import math
dir(math) # math es un módulo con operaciones matemáticas
import math as mt
mt.sin(34) # sin es la función que calcula el seno
0.5290826861200238
mt.sqrt(24)
4.898979485566356
pow(...)
pow(x, y)
Referencias
La librería NumPy es la librería básica de Python para realizar cálculo cientí co. Proporciona una
estructura de datos para representar arrays multidimensionales y un conjunto de funciones y métodos
muy e cientes para operar con dichos arrays. Esta e ciencia se consigue en parte a la eliminación de
recorridos explícitos (for o while) de los elementos del array. Sobre ella se desarrollan otras librerías de
cálculo cientí co de más alto nivel como Pandas o Scipy.
En 1995, Jim Hugunin desarrolló la librería Numeric como una primera aproximación a lo que hoy es
NumPy. Más tarde desarrolló la librería Numarray, mejorando la e ciencia de algunas operaciones sobre
arrays. Durante un tiempo ambas librerías coexistieron, hasta que en el año 2006, el desarrollador Travis
Oliphant lanza la versión 1.0. de NumPy como resultado de fusionar las dos librerías.
Actualmente, NumPy es una de las librerías más usadas en aquellas aplicaciones que involucran arrays
multidimensionales y arrays de gran tamaño. En este capítulo presentamos la estructura de datos
ndarray, la cual permite representar arrays multidimensionales. Veremos cómo crear dichos arrays, sus
propiedades y las operaciones disponibles sobre ellos. Haremos especial hincapié en las llamadas
operaciones vectorizadas, como un mecanismo de cómputo e ciente para este tipo de estructuras.
Importando la librería
Para hacer uso de la librería NumPy es necesario importarla. Para importar la librería NumPy debemos
escribir una de las siguientes instrucciones:
import numpy
import numpy as np
Las dos instrucciones tienen el mismo efecto, con la diferencia de que la segunda permite utilizar las
funciones, datos, etc. de NumPy usando el alias o nombre abreviado (namespace) np en lugar de numpy.
La librería NumPy de ne un nuevo tipo de datos con un conjunto de propiedades (o atributos) y unas
operaciones (o métodos). Se trata del tipo de datos ndarray; nos referiremos a ellos como arrays. Los
arrays de NumPy son colecciones de elementos homogéneos y de tamaño jo. Con el término
homogéneo, nos referimos a que todos los elementos del array tienen el mismo tipo. Esta es una
característica que no tiene por qué cumplirse en el caso de las listas en Python, en las que los elementos
pueden ser de tipos distintos. De tamaño jo porque una vez creados no se pueden redimensionar como
ocurre con las listas.
Para crer un array es necesario utilizar la función np.array. Esta función es la constructora de arrays. A
continuación mostramos cómo crear un array a partir de una lista.
array([[1, 2, 3],
[4, 5, 6]])
La variable datos es de tipo ndarray como se puede observar tras preguntar al intérprete por su tipo:
type(datos)
numpy.ndarray
Cada array de NumPy tiene una serie de propiedades, como por ejemplo, el tipo de datos de los
elementos que contiene, la dimensión del array, etc.
shape que indica las dimensiones del array
dtype indica el tipo de los elementos almacenados en el array
nbytes indica el número de bytes necesarios para almacenar los datos que contiene
ndim indica el número de dimensiones
size indica el número total de elementos en el array
La lista completa de propiedades de los arrays puede obtenerse ejecutando la siguiente instrucción:
np.ndarray?
Podemos consultar las propiedades de un array usando la notación punto . , seguido del nombre de la
propiedad a consultar. A continuación mostramos unos ejemplos de cómo se puede consultar las
características del array datos:
datos.shape
(2, 3)
datos.size
datos.dtype
dtype('int32')
datos.nbytes
24
El array datos contiene 6 elementos de tipo int32, necesitando un total de 24 bytes para poder
almacenarlo.
Los tipos de datos básicos en NumPy para realizar cálculo numérico son int(para enteros), float(para
números en coma otante) y complex(números complejos). Cada uno de estos tipos tiene variantes
dependiendo del tamaño.
oat16: Half precision oat: sign bit, 5 bits exponent, 10 bits mantissa
oat32: Single precision oat: sign bit, 8 bits exponent, 23 bits mantissa
oat64: Double precision oat: sign bit, 11 bits exponent, 52 bits mantissa
También se pueden crear arrays de tipo string.
s.dtype
dtype('<U2')
No suele ser necesario elegir de forma explícita el tamaño del tipo de datos a la hora de crear un array,
pero sí puede ser útil crear arrays de un cierto tipo, ya sea int, float o complex, lo que garantizará la
e ciencia de las operaciones que se vayan a realizar.
En los siguientes ejemplos, mostramos cómo crear arrays cuyos elementos son de tipos int, float y
complex. El tipo de datos complex sirve para representar los números complejos en Python.
[1 2 4]
datos_f = np.array([1, 2, 4], dtype=np.float)
print(datos_f)
[1. 2. 4.]
El tipo de datos de un array no se puede cambiar una vez que se ha creado. Lo que sí podemos hacer es
crear un nuevo array como copia de otro usando la función np.array indicando en el argumento dtype
el nuevo el tipo de datos:
array([1, 2, 4])
Como podemos ver, se ha realizado promoción de tipos; el tipo float ha promocionado al tipo int.
También podemos usar la función astype para crear un array a partir de otro, pero con tipo diferente.
Cuando se opera con arrays, el intérprete de Python realiza promoción automática de tipos si la
operación lo requiere. En el siguiente ejemplo podemos observar que el resultado de la suma de un array
de tipo int y un array de tipo float es un array de tipo float.
resultado.dtype
dtype('float64')
Creación de arrays
En la sección anterior hemos podido ver cómo crear e inicializar arrays mediante la función np.array. A
continuación presentamos otras formas de crear arrays dependiendo de nuestras necesidades. Por
ejemplo, crear arrays con datos que siguen unas reglas, con datos aleatorios o con datos constantes.
También presentamos el mecanismo para crear arrays a partir de datos contenidos en un chero. La lista
completa de funciones se puede consultar en el manual de referencia de NumPy.
a = np.array([3,5,7,9])
a.ndim
b = np.array(a)
b
array([3, 5, 7, 9])
El siguiente ejemplo muestra cómo se crea un array c a partir de un objeto iterable. En este caso se trata
del objeto range(10,14).
c = np.array(range(10,14))
c
Las funciones np.zeros y np.ones permiten crear arrays cuyos datos son todo ceros y unos,
respectivamente. Como argumentos de ambas funciones es necesario indicar las dimensiones del array
mediante una tupla o una lista. Por ejemplo, para crear un array de ceros de dimensión 3 las y 4
columnas, hacemos lo siguiente:
a1 = np.zeros( (3,4) )
a1
a2 = np.ones(7)
a2
a2.ndim, a2.shape
(1, (7,))
Tanto np.zeros como np.ones crean arrays cuyos elementos son por defecto de tipo oat. Para crear
arrays de otro tipo es necesario utilizar el argumento dtype indicando el tipo.
a3 = np.zeros((2,4), dtype=np.int16 )
a3
array([[0, 0, 0, 0],
[0, 0, 0, 0]], dtype=int16)
a4 = np.ones((3,2), dtype=np.float32 )
a4
array([[1., 1.],
[1., 1.],
[1., 1.]], dtype=float32)
a5 = np.zeros_like( a4 )
a5
array([[0., 0.],
[0., 0.],
[0., 0.]], dtype=float32)
a6 = np.ones_like( a4 )
a6
array([[1., 1.],
[1., 1.],
[1., 1.]], dtype=float32)
Si lo que queremos es crear un array cuyos valores son una constante distinta de 0 ó 1, podemos utilizar
la función np.full. En el siguiente ejemplo mostramos cómo crear un array de 5 elementos cuyos
valores son todos 9.5.
b1 = np.full( 7 , 9.5 )
b1
NumPy también ofrece la función fill para rellenar un array previamente creado con un valor
constante.
b2 = np.zeros((2, 5))
b2.fill(10)
b2
NumPy proporciona mecanismos para crear arrays cuyos datos son una secuencia valores equidistantes.
Se trata de las funciones np.arange y np.linspace. Ambas requieren como valor de sus argumentos el
valor inicial y nal de la secuencia. La función np.arange requiere además el incremento o paso,
mientras que la función np.linspace requiere como argumento adicional el número total de valores de
la secuencia. Por ejemplo, para crear una secuencia de valores entre el 1 y el 5 con incremento 0.5
escribimos lo siguiente:
a = np.arange(start = 1, stop = 5, step = 0.5)
print(a)
O lo que es lo mismo:
a = np.arange(1, 5, 0.5)
print(a)
Como se puede observar, la función np.arange no incluye el último valor (en el ejemplo el 5), mientras
que la función np.linspace si lo incluye. Como la función np.arange utiliza argumentos de tipo oat,
no es cómodo predecir el número de elementos del array. En ese caso es mejor utilizar la función
np.linspace que genera un array con un número determinado de elementos sin indicar el incremento.
b = np.linspace(1, 10, 5)
b
El módulo numpy.random ofrece una colección de funciones para generar arrays cuyos datos son valores
aleatorios simples y algunas distribuciones estadísticas. Por ejemplo, la función np.random.rand
premite generar una secuencia de números aleatorios procedentes de una distribución uniforme en el
intervalo [0,1). En el siguiente ejemplo utilizamos la función np.random.rand para generar un único
valor aleatorio:
v = np.random.rand()
v
0.2584688148087325
También podemos crear arrays indicando las dimensiones mediante los argumentos de la función. Por
ejemplo, para generar un array de dimensión 3 las y 4 columnas con valores aleatorios hacemos lo
siguiente:
u2 = np.random.rand(3, 4)
u2
En realidad lo que se genera no son números verdaderamente aleatorios, sino que son pseudoaleatorios.
El generador de números parte de una semilla (normalmente la hora del sistema) y aplicando algoritmos
genera estos números. A veces nos puede interesar jar la semilla para generar la misma secuencia de
números aleatorios (por ejemplo para pruebas). Utilizando la función np.random.seed imponemos las
condiciones iniciales del generador.
np.random.seed(3)
u3 = np.random.rand(4)
u3
La función np.random.randint permite generar valores de tipo int siguiendo una distribución
uniforme en un intervalo dado. En el siguiente ejemplo utilizamos la función np.random.randint para
generar un valor entero aleatorio en el intervalor [0, 10).
ri = np.random.randint(10)
ri
Podemos usar el argumento size para indicar las dimensiones del array a generar, junto con los límites
del intervalo:
array([[20, 20],
[31, 48],
[42, 30]])
O lo que es lo mismo:
array([[39, 49],
[24, 36],
[27, 36]])
La función np.random.randn permte generar números aleatorios siguiendo una distribución normal
(media 0 y desviación estándar 1).
n = np.random.randn()
n
0.8813180422075299
n = np.random.randn(3,2)
n
fig.savefig('./figuras/1_numpy_01.jpg')
En el caso de la función np.random.randn, podemos ver en la gura que los números generados siguen
una distribución normal centrada en el 0.
ax3.hist(np.random.randn(10000))
ax3.set_title('np.random.randn')
fig.savefig('./figuras/1_numpy_02.jpg')
Hasta el momento hemos podido ver diversas alternativas a la creación de arrays, pero no puede faltar
una de las más usadas cuando trabajamos con datos provenientes de fuentes externas y que suele ser el
punto de partida en el proceso de análisis de datos. Se trata de la creación de arrays mediante la lectura
de cheros.
La función np.load de NumPy permite crear arrays a partir de los datos almacenados en un chero en
formato binario:
a = np.load('./datos/pares.npy')
a
array([2, 4, 6, 8])
De forma análoga, la función np.save permite guardar los datos contenidos en un array en un chero en
formato bnario en disco. Estos cheros tienen extensión npy:
np.save('./datos/pares', a)
Para el caso de cheros de texto, NumPy proporciona funciones equivalentes np.load y np.save. Se
trata de las funciones np.loadtxt y np.savetxt. En el siguiente ejemplo mostramos cómo leer el
chero datos/temperaturas.dat y el chero datos/caract.csv.
c = np.loadtxt('./datos/temperaturas.dat')
c
array([ 3.1, 3.3, 6.2, 9.2, 13.1, 15.6, 17.9, 17.5, 14.5, 10.7, 6.7,
3.7])
d = np.loadtxt('./datos/caract.csv', delimiter=',')
d
array([[ 89.2, 3. ],
[ 88.3, 4. ],
[ 90. , 5. ],
[100. , 6. ],
[ 85.4, 7. ],
[103. , 8. ],
[ 91.2, 9. ]])
Estas funciones ofrecen muchas posibilidades, como por ejemplo usar diferentes delimitadores, aplicar
funciones de conversión en alguna de sus columnas, saltar las, omitir columnas, etc. En el capítulo
dedicado a Pandas veremos estas opciones con más detalle. En el siguiente ejemplo mostramos cómo
leer una sola columna del chero datos/caract.csv:
En el siguiente ejemplo mostramos cómo guardar los datos de un array en un chero de texto. Por
ejemplo, aplicamos la función np.cumsum para crear un array con la suma acumulada del array d y
guardamos esos datos en un nuevo chero de texto.
suma_acumulada = np.cumsum(d)
suma_acumulada
La función np.genfromtxt tiene más funcionalidad que np.loadtxt para el caso de cheros en
formato CSV. Por ejemplo, permite leer la línea de cabecera si existe y acceder a los datos del array por el
nombre. Por ejemplo, para leer el chero ‘medidas.csv’ (ver Fichero datos/medidas.csv), usamos la
función np.genfromtxt usando como argumentos, el nombre del chero y el delimitador. Si el chero
trae cabeceras, lo indicamos mediante el argumento name con valor True.
mmm
De esta forma, es posible acceder a los datos mediante el nombre de las columnas.
mmm['Altura']
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial Garceta.
ISBN: 978-84-1622-883-6
El acceso a los elementos (o subarrays) de un array se realiza de forma similar a como se hace en el caso
de listas, es decir, usamos la notación de corchetes. En general, la expresión entre corchetes es una tupla,
donde cada elemento de la tupla impone las condiciones de selección para cada una de las dimensiones
del array.
A continuación mostramos algunos ejemplos. Partimos de un array de una dimensión que contiene
elementos entre el 3 y el 11:
import numpy as np
arr = np.arange(3,12)
arr
arr[0]
arr[2]
arr[-1]
11
Para seleccionar un rango de elementos, por ejemplo los elementos entre el segundo y quinto elemento,
o los tres últimos elementos, escribimos los siguiente:
arr[1:4]
array([4, 5, 6])
arr[-3:]
Para seleccionar todos los elementos en órden inverso, o todos los elementos en orden inverso con
incremento 2, escribimos lo siguiente:
arr[::-1]
arr[::-2]
array([11, 9, 7, 5, 3])
Arrays multidimensionales
El acceso a los elementos de un array multidimensional se realiza aplicando a cada una de sus
dimensiones, el procedimiento visto para arrays de una dimensión.
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
arr[1,:]
array([5, 6, 7, 8])
arr[:,1]
array([ 2, 6, 10])
arr[:2, 2:]
array([[3, 4],
[7, 8]])
Una diferencia importante con respecto a las listas, es que las particiones de un array en NumPy
mediante la notación m:n:p son vistas del array original. Todos los cambios realizados en las vistas,
afectan al array original. En el siguiente ejemplo creamos un array a mediante el rango de valores entre 1
y 10. Posteriormente creamos el array b a partir de a:
a = np.arange(1, 11)
a
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
b = a[:4]
b
array([1, 2, 3, 4])
Los elementos en el array b no son una copia de los cuatro primeros elementos de a, sino que ambos
arrays hacen referencia a los mismos elementos en memoria, lo que implica que todos los cambios en b
afectan también al array a:
b[::] = 0
a
array([ 0, 0, 0, 0, 5, 6, 7, 8, 9, 10])
a[2] = 7
b
array([0, 0, 7, 0])
Hay que recordar que NumPy ha sido diseñado para manejar grandes cantidades de datos y este
comportamiento evita problemas de memoria. En cualquier caso, si es necesario realizar la copia de un
array o de partes de él, podemos usar la función np.copy:
c = np.copy(a[5:])
c
array([ 6, 7, 8, 9, 10])
c[:] = 1
c
array([1, 1, 1, 1, 1])
array([ 0, 0, 7, 0, 5, 6, 7, 8, 9, 10])
array([ 0, 0, 7, 0, 5, 6, 7, 8, 9, 10])
a[[0,5,7]]
array([0, 6, 8])
O lo que es lo mismo:
m = np.array([0,5,7])
a[m]
array([0, 6, 8])
En los ejemplos anteriores, tanto la lista [0,5,7] como el array m actúan como ltros para la selección de
elementos del array a.
Otra forma de seleccionar partes de un array es mediante el uso de arrays de elementos de tipo bool. En
este caso, cada elemento del array (con valores True o False) indica si el elemento del array original con
el mismo índice, se selecciona o no. Supongamos que queremos seleccionar los elementos ´mayores que
7 del array b, donde b se crea como un rango de valores entre el 0 y el 11:
b = np.arange(12)
b
La siguiente expresión aplica el operador de comparación > a cada uno de los elementos del array b,
produciendo como resultado un array de elementos de tipo bool. Este array es el que actuará como
ltro:
b > 7
Ahora, realizamos la selección de los elementos de b utilizando el array generado por la expresión b > 7:
b[b > 7]
Cuando trabajamos con arrays de datos, puede ser útil modi car la forma de un array sin cambiar los
datos que contiene. Por ejemplo, un array de 2 las y 2 columnas se puede transformar en un array
unidimensional de 4 elementos. En NumPy, la función np.reshape permite cambiar las dimensiones de
un array sin modi car los datos que contiene. Veamos un ejemplo:
array([[2, 4, 6],
[1, 2, 3]])
array([2, 4, 6, 1, 2, 3])
Esta operación también se encuentra de nida como una operación de los arrays, por lo que también
puede invocarse sobre una variable de tipo array sando la notación punto ., seguido del nombre de la
operación:
arr.reshape(6) # operación (o método) reshape definida para ser ejecutada sobre un array
array([2, 4, 6, 1, 2, 3])
La función np.reshape recibe como argumentos un array y la nueva dimensión. Por supuesto, es
necesario que la dimensión del array sea compatible con el número de elementos del array.
np.reshape(arr,(3,2))
array([[2, 4],
[6, 1],
[2, 3]])
array([[2, 4, 6],
[1, 2, 3]])
m.ravel() # operación (o método) ravel definida para ser ejecutada sobre un array
array([2, 4, 6, 1, 2, 3])
array([2, 4, 6, 1, 2, 3])
Hay que tener en cuenta que las funciones np.reshape y np.ravel devuelven como resultado una vista
del array original, mientras que la función np.flatten devuelve una copia.
t = m.flatten()
t
array([2, 4, 6, 1, 2, 3])
En el caso de necesitar construir un array a partir de otros arrays existentes, NumPy proporciona las
funciones np.vstack y np.hstack. La función np.vstack permite concatenar una secuencia de arrays
verticalmente, mientras que la función np.vstack lo hace horizontalmente. Para que ambas operaciones
tengan éxito, las dimensiones de los arrays involucrados han de ser compatibles. Veamos algunos
ejemplos:
a = np.random.randint(0,9, (2,4))
a
array([[8, 4, 0, 0],
[8, 3, 8, 8]])
b = np.random.randint(0,2 ,(2,2))
b
array([[1, 0],
[1, 0]])
c = np.random.randint(5,9 ,(2,2))
c
array([[5, 7],
[7, 7]])
Los arrays a y b pueden concatenarse horizontalmente pero no verticalmente, mientras que los arrays b
y c pueden concatenarse verticalmente pero no horizontalmente.
np.hstack((a,b))
array([[8, 4, 0, 0, 1, 0],
[8, 3, 8, 8, 1, 0]])
np.vstack((b,c))
array([[1, 0],
[1, 0],
[5, 7],
[7, 7]])
El número de elementos del array no puede cambiar una vez ha sido creado. Para insertar y eliminar
elementos de un array, NumPy proporciona las funciones np.insert, np.append y np.delete. Todas
ellas devuelven una copia del array original.
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial Garceta.
ISBN: 978-84-1622-883-6
Operaciones vectorizadas
Los arrays realizan una gestión de la memoria mucho más e ciente que las listas y por tanto se mejora el
rendimiento. NumPy implementa una gran variedad de operaciones y funciones matemáticas mediante
expresiones sencillas sobre arrays (expresiones vectorizadas). El uso de estas expresiones permite operar
sobre los elementos del array sin necesidad de escribir múltiples y costosos loops.
Operaciones Aritméticas
Los operadores aritméticos sobre arrays, son operadores que se aplican elemento a elemento. El
resultado es un nuevo array cuyos datos depende de la operación realizada. Veamos algunos ejemplos:
import numpy as np
a = np.array([1,2,3], int)
b = np.array([5,2,6], int)
a + b
array([6, 4, 9])
a - b
array([-4, 0, -3])
a * b
array([ 5, 4, 18])
En el caso de operaciones entre arrays y escalares, el escalar se aplica a cada uno de los elementos del
array:
a * 10
b ** 2
array([[1, 2],
[3, 4]])
array([[2, 0],
[1, 3]])
a * b
array([[ 2, 0],
[ 3, 12]])
a - b
array([[-1, 2],
[ 2, 1]])
En el caso de las operaciones entre arrays, NumPy comprueba la compatibilidad de las dimensiones
antes de realizar la operación. Si los arrays involucrados tienen los mismos valores para todas sus
dimensiones, la operación se realiza sin problemas. En caso contrario, NumPy comprueba que el valor
sea 1 para todas las dimensiones con valores distintos. Porteriormente se extienden los arrays hasta
conseguir que los arrays tengan los mismos valores en todas sus dimensiones y nalmente realiza la
operación. Esta operación de extensión de arrays se la conoce como broadcasting.
Por ejemplo, en el caso de arrays bidimensionales, el array m y n son compatibles y se pueden sumar:
(2, 2)
n = np.array([[2],[3]], int)
n.shape
(2, 1)
m + n
array([[3, 4],
[6, 7]])
En el ejemplo anterior se puede ver cómo la operación suma tiene éxito con los arrays m y n con
dimensiones (2, 2) y (2, 1) respectivamente. La siguiente gura muestra cómo se realiza la operación de
extensión de dimensiones (conocida como broadcasting) antes de realizar la suma de los arrays.
La segunda dimensión del array m tiene valor 2, mientras que la segunda dmensión del array n es 1. El
valor es distinto para la segunda dimensión, pero la operación se puede realizar extendiendo
previamente la segunda dimensión del array n.
n1 = np.array([[2],[3]], int)
n1.shape
(2, 1)
(1, 2)
n1 + n2
array([[4, 5],
[5, 6]])
Puede darse el caso de que los arrays no tengan ni siquiera el mismo número de dimensiones. En ese
caso NumPy asigna valor 1 a las dimensiones no existentes.
m1 = np.array([[1, 2], [3,4]], int)
m1
array([[1, 2],
[3, 4]])
m1.shape
(2, 2)
array([5, 2])
m1.shape
(2, 2)
m1 * m2
array([[ 5, 4],
[15, 8]])
El producto matricial se obtiene mediante el uso de la función np.dot o creando objetos de tipo matrix
en lugar de ndarray.
A = np.array( [[1,1],[0,1]] )
B = np.array( [[2,0],[3,4]] )
M = np.dot(A,B)
M
array([[5, 4],
[3, 4]])
Funciones Universales
Se trata de funciones que se aplican a cada uno de los elementos de un array. Cada una de estas
funciones recibe un array de entrada y devuelve como resultado otro array de la misma forma. Cada uno
de los elementos del array devuelto, es el resultado de aplicar la función al correspondiente elemento en
el array de entrada. Muchas de estas funciones son de aridad 1, como np.sqrt o np.cos:
a = np.array([0, 1, 4, 9, 16])
a
array([ 0, 1, 4, 9, 16])
Para calcular la raiz cuadrada de cada uno de los elementos del array escribimos lo siguiente:
np.sqrt(a)
Para calcular el coseno de cada uno de los elementos del array escribimos lo siguiente:
np.cos(a)
np.square(a)
Otras funciones son de aridad 2, como por ejemplo np.maximum, np.power o np.greater_equal:
np.maximum(a,b)
np.power(b,3)
np.greater_equal(a,b)
Funciones trigonométricas
Funciones logarítmicas
np.sqrt
np.exp
np.power
np.remainder
np.sign, np.abs
Funciones de Agregación
Las funciones de agregación en NumPy reciben como argumento de entrada un array y devuelven como
resultado un valor escalar. El cálculo del máximo valor de un array np.max, la suma de sus elementos
np.sum o las funciones estadísticas como por ejemplo la media np.meano la desviación típica np.std
son ejemplos de funciones de agregación. Todas ellas también se encuentran disponibles como
operaciones de los arrays.
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
O lo que es lo mismo:
5.0
datos.prod()
362880
np.remainder(datos,2)
Por defecto, las funciones de agregación se aplican sobre el conjunto de todos los elementos del array de
entrada. Es posible indicar mediante el argumento axis, el eje sobre el cuál la función de agregación
debe aplicarse. Por ejemplo, en el caso de arrays de dos dimensiones, podemos calcular la suma
completa de los elementos del array o la suma de los elementos con respecto al eje 0 o el eje 1.
datos = np.arange(1,10)
datos = datos.reshape(3,3)
datos
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
datos.sum()
45
datos.sum(axis = 0)
datos.sum(axis = 1)
Las operaciones de comparación de dos array devuelven arrays de tipo bool. NumPy proporciona
funciones capaces de operar con los resultados de las operaciones de comparación entre arrays.
Ejemplos de estas funciones son np.all y np.any:
x = np.array([1, 2, 3, 4, 5])
y = np.array([5, 9, 8, 1, 7])
x < y
El resultado de la comparación es un array de elementos de tipo bool. La función np.all devuelve valor
True si todos los elementos en x < y son True.
np.all( x < y )
False
La función np.any devuelve valor True si alguno de los elementos en x < y es True.
np.any( x < y )
True
La ventaja del uso de estas funciones es que en la mayoría de los casos permiten prescindir de
instrucciones condicionales del tipo if ... then ...
Expresiones Condicionales
x = np.array([1, 2, 3, 4])
y = np.array([5, 6, 7, 8])
cond = np.array([True, False, False, True])
La función np.where selecciona elementos de dos arrays (segundo y tercer argumento) dependiendo del
valor de un array de tipo bool (primer argumento). Si la condición es cierta, el resultado es el elemento en
el array del segundo argumento. Si la condición es falsa, el resultado es el elemento en el array del tercer
argumento.
np.where(cond, x, y)
array([1, 6, 7, 4])
En realidad, los dos últimos argumentos de la operación np.where no tienen por qué ser arrays, pueden
ser escalares.
np.where(cond, x, 0)
array([1, 0, 0, 4])
Ejemplo:
Supongamos que queremos sustituir los valores negativos de un array por 0. Los valores positivos no se
ven modi cados.
a = np.array([[4, 5, -1], [8, 6, -3], [-5, -4, 3]])
a
array([[ 4, 5, -1],
[ 8, 6, -3],
[-5, -4, 3]])
np.where(a >0 , a, 0)
array([[4, 5, 0],
[8, 6, 0],
[0, 0, 3]])
La condición a > 0 se evalúa para cada elemento del array a (recordar que las operaciones son
vectorizadas). Si la condición es cierta, la función where devuelve ese elemento. En caso contrario
devuelve 0.
Ejemplo:
Supongamos que queremos transformar los valores negativos en positivos. Los valores positivos no se
ven modi cados.
array([[ 4, 5, -1],
[ 8, 6, -3],
[-5, -4, 3]])
array([[4, 5, 1],
[8, 6, 3],
[5, 4, 3]])
La operación anterior también se podría obtener usando la función np.abs de la librería NumPy.
array([[ 4, 5, -1],
[ 8, 6, -3],
[-5, -4, 3]])
np.abs(a)
array([[4, 5, 1],
[8, 6, 3],
[5, 4, 3]])
Operaciones de Conjunto
La siguiente tabla describe algunas de las operaciones de conjunto más usadas en el caso de arrays de
una dimensión. El uso de arrays para describir conjuntos de datos y operar sobre ellos, permite escribir
ciertas operaciones mediante expresiones vectorizadas.
Función Descripción
np.union1d Crea un nuevo array cuyos elementos están en uno de los dos arrays o en ambos
np.intersect1d Crea u nuevo array cuyos elementos están en los dos arrays
array([1, 2, 3, 4, 5, 6])
Por otro lado, la función np.in1d comprueba la pertenencia de los elementos de un array en otro array.
Esta operación se realiza elemento a elemento y devuelve como resultado un array de tipo bool.
b = np.array([6, 1, 0, 5 ])
np.in1d(b, a)
np.all(np.in1d(b,a))
False
np.union1d(a,b)
array([0, 1, 2, 3, 4, 5, 6])
np.intersect1d(a,b)
array([1, 5, 6])
Orden
Al igual que las listas, es posible ordenar arrays mediante la función np.sort o la operación sort de los
arrays. La función recibe como argumento un array y devuelve un nuevo array ordenado. Sin embargo, la
operación sort no devuelve un nuevo array, si no que modi ca el array a ordenar.
arr = np.random.randint(1,10, 6)
arr
array([2, 4, 6, 2, 9, 3])
np.sort(arr)
array([2, 2, 3, 4, 6, 9])
arr
array([2, 4, 6, 2, 9, 3])
arr.sort()
arr
array([2, 2, 3, 4, 6, 9])
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Libro del autor de NumPy: http://csc.ucdavis.edu/~chaos/courses/nlp/Software/NumPyBook.pdf
https://docs.scipy.org/doc/numpy/reference/routines.html En este enlace podemos encontrar el
repertorio de funciones y métodos implementados en la librería NumPy. Todos ellos se
encuentran clasi cados por categorías. Además se presentan ejemplos de uso para facilitar su
comprensión.
Lista completa de funciones universales https://docs.scipy.org/doc/numpy/reference/ufuncs.html
Sección 3.5
Ejemplo 1
Tanto los arrays de NumPy como las listas de Python, sirven para almacenar colecciones de datos. La
principal diferencia entre ambas estructuras de datos radica en las operaciones que se realizan sobre
ellas.
En primer lugar, creamos una lista y un array de NumPy con las funciones range y np.arange
respectivamente.
import numpy as np
lista = range(1000)
arr = np.arange(1000)
Supongamos que queremos calcular la suma acumulada de una colección de elementos. La función
sumaAcumulada1 recibe como argumento una lista y resuelve el problema iterando sobre cada uno de
los elementos de las lista usando el iterador for.
def sumaAcumulada1(l):
r = [l[0]]
for i in range(1,len(l)):
r.append(r[i-1]+l[i])
return r
Por otro lado, la función sumaAcumulada2 recibe como argumento un array de NumPy y devuelve como
resultado la suma acumulada de los elementos del array usando la función np.cumsum de la librería
NumPy.
def sumaAcumulada2(a):
return np.cumsum(a)
Como se puede observar, la función sumaAcumulada2 no necesita iterar sobre los elementos del array
mediante la instrucción for. A continuación mostramos los tiempos que consumen ambas funciones en
ejecutarse. Usamos el comando mágico %timeit.
%timeit r1 = sumaAcumulada1(lista)
246 µs ± 9.77 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit r2 = sumaAcumulada2(arr)
4.29 µs ± 158 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Ejemplo 2
Los datos contenidos en el chero datos.txt describe las poblaciones de tres tipos de serpientes en el sur
de Africa durante 20 años.
import numpy as np
data = np.loadtxt('./datos.txt') # carga de los datos
data
poblaciones = data[:, 1:] # nos quedamos con las tres últimas columnas
poblaciones
poblaciones.mean(axis = 0) # media de las filas (axis = 0) para cada una de las columnas
Mostrar los resultados expresado en miles de ejemplares. Tendremos que dividir por mil el
array.
poblaciones = poblaciones / 1000
poblaciones
array([[30. , 3. , 38.3],
[37.2, 6.1, 48.2],
[60.2, 9.8, 31.6],
[77.3, 36.2, 38.2],
[36.3, 69.3, 30.6],
[20.6, 31.7, 49.8],
[18.1, 19. , 38.6],
[21.3, 13. , 32.3],
[22. , 8.3, 43.6],
[26.3, 9.1, 32.1],
[27.1, 7.3, 36. ],
[30.3, 8. , 36.8],
[67. , 12.3, 33.8],
[76.6, 19.6, 30.9],
[62.3, 36.7, 49.3],
[19.6, 61.1, 39. ],
[11.2, 29.7, 36.7],
[ 7.6, 16.8, 41.8],
[13.6, 9.7, 33.3],
[16.2, 10.1, 31.3],
[23.7, 8.6, 47.3]])
poblaciones = np.around(poblaciones)
poblaciones
poblaciones.std(axis=0)
# máximo de cada año (máximo de las columnas (axis = 1) para cada una de las filas)
maximo= poblaciones.max(axis = 1)
maximo
array([38., 48., 60., 77., 69., 50., 39., 32., 44., 32., 36., 37., 67.,
77., 62., 61., 37., 42., 33., 31., 47.])
especie = np.argmax(poblaciones, axis=1) # posición que ocupa el máximo valor
especie
# columna 0 se refiere a la especie 1
# columns 1 se refiere a la especie 2
# columns 2 se refiere a la especie 3
array([2, 2, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 2, 2, 2, 2],
dtype=int64)
# creamos un diccionario
nombres = {0 : 'Especie 1 ', 1: 'Especie 2', 2: 'Especie 3'}
Ejercicios
Los enlaces que te encuentres en esta página solo funcionan desde el formato html, no desde el
notebook una vez descargado.
Ejercicio 1
Disponemos de dos cheros que contienen mediciones de temperatura y humedad relativa del aire en
diferentes puntos de una zona geográ ca. Si observamos los datos, se trata de una matriz de celdas (o
píxeles) organizadas en las y columnas (o una cuadrícula) en la que cada celda contiene un valor que
representa cierta información. En este caso, la temperatura y la humedad relativa.
a) Crear un array de numpy con los datos de las temperaturas de la zona geográ ca de estudio. El array
ha de llamarse temperatura.
# Sol:
b) Crear un array de numpy con los datos de las mediciones de humedad de la zona geográ ca de
estudio. El array ha de llamarse humedad.
c) Determinar el tamaño de los datos. ¿Cuántas las y cuántas columnas tiene el array temperatura?,
¿Cuantas las y cuántas columnas tiene el array humedad?
# sol:
# Ambos arrays son del mismo tamaño, 133 filas y 133 columnas
# SOl:
# temperatura máxima: 31
# temperatura máxima: 10
e) El índice de temperatura-humedad, es un número utilizado para indicar la falta de confort causada por
los efectos combinados de la temperatura y la humedad del aire. El índice fue desarrollado en 1959 por
Earl C. Thom, un investigador de la O cina de Climatología del US Weather Bureau. La fórmula para
calcular el índice de temperatura-humedad (ITH) es:
𝐼𝑇𝐻 𝑡𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑎
= 0.8 × +
ℎ𝑢𝑚𝑒𝑑𝑎𝑑𝑟𝑒𝑙𝑎𝑡𝑖𝑣𝑎
100
×(𝑡𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑎 − 14.3) + 46.4
Calcula el índice de temperatura-humedad (ITH) de cada una de las celdas de la cuadrícula. El resultado
debe ser un array bidimensional con el mismo número de las y columnas que los arrays temperatura y
humedad. El nombre de este array debe ser ith.
Para calcular el índice de temperatura humedad puedes seguir los siguientes pasos:
# Sol:
f) Redondea los valores del array obtenido en el apartado anterior (ith) al entero más cercano. Puedes
usar la función universal np.around. Para obtener información de uso de la función np.around ejecuta
la siguiente celda o consulta la ayuda en
https://numpy.org/doc/stable/reference/generated/numpy.around.html
# ejecuta esta celda y busca el ejemplo de uso
np.around?
# Sol:
# ith = array([[60., 60., 59., ..., 64., 64., 64.],
# [64., 64., 64., ..., 70., 68., 68.],
# [70., 70., 70., ..., 64., 64., 64.],
# ...,
# [62., 62., 62., ..., 65., 65., 65.],
# [65., 65., 63., ..., 64., 64., 64.],
# [64., 66., 66., ..., 64., 64., 64.]])
# Sol:
h) En la actualidad este índice se utiliza para supervisar las condiciones de estrés por calor a las que está
expuesto el ganado bovino, y su correlato en la producción lechera y su e ciencia reproductiva. En
general los expertos han determinado que con un índice ITH inferior a 72 el ganado lechero no
experimenta estrés, si el IHT se entre 72 y 78 el estrés es moderado y valores ITH de entre 78 y 88 se
caracterizan por un estrés grave.
Determina si se produce estrés en algún punto de la cuadrícula, es decir, si existe algún valor mayor a 72.
¿Se produce estrés grave en algún punto?
# Sol:
Table of Contents
Sección 4.1
La librería Pandas proporciona estructuras de datos de alto nivel que permiten representar series y
tablas de datos, así como herramientas diseñadas especí camente para un tratamiento de datos rápido y
sencillo. Está construida sobre los fundamentos de NumPy, adaptando las operaciones sobre arrays a las
nuevas estructuras de nidas en Pandas. Resulta especialmente útil cuando hay que trabajar con datos
heterogéneos representados de forma tabular.
Empezó a desarrollarse en el año 2008 por Wes McKinney (McKinney, 2012) en un intento por conseguir
una herramienta exible y de alto rendimiento para el tratamiento de datos nancieros. En el año 2012,
Chang She comenzó a participar en el desarrollo de la librería. Actualmente Pandas forma parte de un
proyecto en el que participan muchos desarrolladores.
Pandas es actualmente una de las librerías de alto nivel de Python más utilizadas en las primeras etapas
del proceso de análisis de datos, especialmente apropiada en aplicaciones estadísticas.
En esta Sección introducimos las estructuras principales de Pandas, como son las Series y DataFrames,
así como las operaciones básicas que se pueden realizar con dichas estructuras.
Importar librerías
En esta Sección trabajaremos principalmente con las librerías Pandas, NumPy y la librería básica para
visualización Matplotlib. Importamos las tres librerías con alias pd, np y plt respectivamente:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
En Pandas disponemos de varias estructuras de datos y operaciones (métodos) que nos ayudarán a
manipular dichas estructuras de forma sencilla y e ciente. Las dos estructuras de datos principales son
las Series y los DataFrames, capaces de representar series de datos y datos en forma tabular
respectivamente.
Series
Una variable del tipo Series puede verse como una variable de tipo array de una dimensión que
contiene datos de un cierto tipo y tiene asociado un índice. Este índice es un array de etiquetas cuyo
tamaño coincide con el tamaño del array de datos.
A continuación creamos una variable s de tipo Serie mediante la función pd.Series y una lista de
elementos:
0 10
1 20
2 30
3 40
4 -10
dtype: int64
s.index
s.values
Una de las ventajas de trabajar con Series, es que es posible indexar cada elemento de la serie con un
valor descriptivo o etiqueta. Por ejemplo, si nuestros datos representan la tasa de natalidad de distintas
ciudades españolas, sería interesante usar el nombre de las ciudades cómo etiquetas del índice, en lugar
de usar etiquetas de tipo int:
nat
Murcia 10.7
Cantabria 7.5
Galicia 7.1
Melilla 17.8
Canarias 7.9
Name: Tasa de Natalidad, dtype: float64
El argumento name de la función pd.Series permite asignar un nombre a la serie creada. Otra de las
propiedades de las Series es name:
nat.name
'Tasa de Natalidad'
En este caso, tenemos dos procedimientos para acceder a los datos de una serie. El primero es mediante
la etiqueta del índice y notación de corchetes, como en el caso de las listas o los arrays:
nat['Murcia']
10.7
El segundo es mediante la etiqueta del índice (siempre que sea un nombre válido en Python) como si
fuera una propiedad de la serie:
nat.Murcia
10.7
Está claro que los datos los podríamos haber representado con un array en lugar de una serie, pero como
se puede ver, las series proporcionan mayor claridad en cuanto a los datos que contienen, la forma de
acceso, etc. Por ejemplo, es posible preguntar de forma natural si un determinado valor está en el índice:
'Cantabria' in nat.index
True
En cuanto a las operaciones con series, se mantienen muchas de las propiedades vistas para los arrays
de NumPy. Por ejemplo, las operaciones aritméticas (+, - , * , /) son vectorizadas, manteniéndose el valor
del índice de la serie:
nat * 100
Murcia 1070.0
Cantabria 750.0
Galicia 710.0
Melilla 1780.0
Canarias 790.0
Name: Tasa de Natalidad, dtype: float64
Las funciones de nidas en la librería Numpy pueden ser utilizadas con series de Pandas. El argumento de
la función puede ser una serie o un array.
nat
Murcia 10.7
Cantabria 7.5
Galicia 7.1
Melilla 17.8
Canarias 7.9
Name: Tasa de Natalidad, dtype: float64
np.ceil(nat)
Murcia 11.0
Cantabria 8.0
Galicia 8.0
Melilla 18.0
Canarias 8.0
Name: Tasa de Natalidad, dtype: float64
Otra de las operaciones que se aplican a los arrays de Numpy y que se extienden a las series de Pandas,
es la selección de elementos usando arrays de tipo bool.
Cantabria 7.5
Galicia 7.1
Canarias 7.9
Name: Tasa de Natalidad, dtype: float64
La característica estrella de las series es el alineamiento de datos en base al valor de los índices en las
operaciones entre dos series:
nach
Cantabria 2154
Canarias 8184
Galicia 10032
Melilla 780
Murcia 8177
Name: Nacidos Hombres, dtype: int64
nacm
Canarias 7962
Cantabria 2221
Melilla 730
Galicia 9395
Name: Nacidas Mujeres, dtype: int64
nach + nacm
Canarias 16146.0
Cantabria 4375.0
Galicia 19427.0
Melilla 1510.0
Murcia NaN
dtype: float64
El resultado de la operación anterior contiene el valor NaN. Este valor se usa para representar un campo
vacío o desconocido. En este caso, la serie nach tiene valor 8177 para la etiqueta Murcia, pero la serie
nacm no contiene dicha etiqueta en su índice, por lo que la suma de ambos produce como resultado NaN.
Aunque los valores NaN generan algunos problemas a la hora de realizar ciertos análisis, puede ser útil
de nir una serie a partir de un array que contenga uno o varios valores NaN.
nacm
Canarias 7962.0
Cantabria NaN
Melilla 730.0
Galicia NaN
Name: Nacidas Mujeres, dtype: float64
Los métodos isnull y notnull de las Series permiten identi car aquellas etiquetas en el índice
asociadas a un valor NaN. Como resultado se obtiene una nueva serie cuyos valores son de tipo Bool.
nacm.isnull()
Canarias False
Cantabria True
Melilla False
Galicia True
Name: Nacidas Mujeres, dtype: bool
nacm.notnull()
Canarias True
Cantabria False
Melilla True
Galicia False
Name: Nacidas Mujeres, dtype: bool
El análisis descriptivo de los datos representados mediante una serie se puede realizar usando los
métodos mean (media), median (mediana), std(desviación estándar), min(mínimo), max(máximo) y
quantile(quantiles) o usando el método describe de las Series:
nach.mean(), nach.median(), nach.std()
nach.describe()
count 5.000000
mean 5865.400000
std 4114.478679
min 780.000000
25% 2154.000000
50% 8177.000000
75% 8184.000000
max 10032.000000
Name: Nacidos Hombres, dtype: float64
Como complemento a la información proporcionada por el método describe, la librería Pandas junto
con Matplotlib permiten la creación de los grá cos más comunes en estadística descriptiva. Para crear un
diagrama de barras (bar) y un diagrama de tarta (pie) escribimos lo siguiente:
La variable figura representa el lienzo sobre el que pintaremos uno o varios grá cos. En el ejemplo
anterior, pintamos un único grá co llamado ax1.
La gestión de valores duplicados se realiza mediante los métodos unique y value_count de las Series.
El método unique devuelve un array con los valores de la serie sin duplicados. A continuación creamos
una serie con valores duplicados:
pos
Cádiz Sur
Cantabria Norte
Melilla Sur
Galicia Norte
Asturias Norte
dtype: object
pos.unique()
pos.value_counts()
Norte 3
Sur 2
dtype: int64
Los DataFrame de Pandas están diseñados para manejar datos representados en forma de tabla, donde
tanto las las como las columnas están indexadas. Los datos de tipo DataFrame pueden verse como una
colección ordenada de columnas, cada una de las cuales viene representada por un objeto de tipo
Series, con su nombre y su índice. Cada columna tiene asociado un tipo de datos y todas las columnas
comparten el mísmo índice. En este caso, usaremos el término dataframe para referirnos a los datos de
tipo DataFrame.
Podemos usar la función pd.DataFrame si los datos se encuentran representados mediante una
lista, un diccionario (de listas o de arrays), una Serie o incluso otro DataFrame.
Si los datos en encuentran en un chero, Pandas proporciona las funciones pd.read_csv,
pd.read_excel para la creación de DataFrame.
En el siguiente ejemplo usamos la función pd.DataFrame para crear un dataframe a partir de un
diccionario:
Ciudades Nacidos H
0 Melilla 780
1 Canarias 8184
2 Galicia 10032
3 Cantabria 2154
4 Murcia 8177
Las claves del diccionario forman el índice de las columnas. El valor de cada una de las claves del
diccinario ha de ser una lista o un array de igual longuitud. Al igual que las Series, los atributos index y
values representan el índice y los datos del dataframe respectivamente:
tabla.index
tabla.values
array([['Melilla', 780],
['Canarias', 8184],
['Galicia', 10032],
['Cantabria', 2154],
['Murcia', 8177]], dtype=object)
A diferencia de las series, que tienen un único índice, los dataframes tienen dos índices. El primero es un
índice común para todas las columnas y está asociado a cada una de las las. El segundo índice contiene
un array de etiquetas o nombres de columnas. La propiedad columns representa el índice de las
columnas:
tabla.columns
nat
Murcia 10.7
Cantabria 7.5
Galicia 7.1
Melilla 17.8
Canarias 7.9
Name: Valor, dtype: float64
tabla2 = pd.DataFrame(nat)
tabla2
Valor
Murcia 10.7
Cantabria 7.5
Galicia 7.1
Melilla 17.8
Canarias 7.9
El índice del DataFrame creado coincide con el índice de la serie nat y el nombre de la columna es el
nombre de la Serie.
Una forma de añadir más información al dataframe es dar nombre a cada uno de los índices.
tabla2.index.name = 'Comunidades'
tabla2.columns.name = 'Tasa de Natalidad'
tabla2
Comunidades
Murcia 10.7
Cantabria 7.5
Galicia 7.1
Melilla 17.8
Canarias 7.9
La función pd.DataFrame admite más argumentos, como por ejemplo los argumentos index y columns
para crear el índice de las las y columnas del DataFrame respectivamente. En el siguiente ejemplo
creamos un DataFrame con datos acerca de la producción de mantequilla en miles de toneladas de
ciertos paises en distintos años (fuente: http://ec.europa.eu/eurostat/web/agriculture/data/main-tables):
El método info de los datos de tipo DataFrame muestra información acerca del objeto DataFrame (el
número de columnas, el número de las, la cantidad de memoria usada), e información relativa a cada
una de las columnas (el nombre de las columnas, su tipo y el número de valores distinto de NaN).
tabla3.info()
<class 'pandas.core.frame.DataFrame'>
Index: 5 entries, EE to FR
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Desc 5 non-null object
1 2004 5 non-null float64
2 2005 5 non-null float64
3 2006 5 non-null float64
4 Tipo 5 non-null object
dtypes: float64(3), object(2)
memory usage: 240.0+ bytes
El método describe muestra los primeros estadísticos, lo que permite hacer un análisis de los datos en
cada una de las columnas numéricas.
tabla3.describe()
Los datos mostrados son la media (mean), la mediana (median), la desviación estándar(std), el minimo
(min), el máximo (max) y los quantiles (quantile).
figura, ax1 = plt.subplots(1, figsize = (9,3))
Al igual que las Series, los DataFrame también disponen de métodos estadísticos (mean, std, min, etc).
Estos métodos devuelven un valor solo para aquellas columnas numéricas del objeto DataFrame.
tabla3.mean()
2004 133.782
2005 143.812
2006 135.298
dtype: float64
tabla3.std()
2004 175.270242
2005 180.976401
2006 173.191749
dtype: float64
Todas las operaciones estadísticas admiten el argumento axis, el cual permite operar bien sobre los
datos de las columnas o bien sobre los datos en las las.
tabla3.sum(axis = 1)
EE 22.80
IE 642.31
EL 4.50
ES 156.14
FR 1238.71
dtype: float64
Una operación muy interesante cuando tratamos con datos representados mediante dataframes, es la de
trasponer, es decir, cambiar las por columnas. Para trasponer un dataframe es necesario utilizar el
atributo T.
tabla3.T
EE IE EL ES FR
Tipo A B A C A
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python Data Analysis Library
Python for Data Analysis
Table of Contents
Sección 4.2
En esta Sección mostramos los mecanismos para acceder a los datos contenidos en una serie o un
dataframe, así como los mecanismos para realizar su actualización.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
La variable tabla3 represeta un DataFrame con datos acerca de la producción de mantequilla en miles
de toneladas de ciertos paises en distintos años.
tabla3 = pd.DataFrame( data = [ ('Estonia', 8.4, 7.7, 6.7, 'A'),
('Ireland',193.5, 227.76, 221.05, 'B'),
('Greece',1.4,1.6,1.5, 'A'),
('Spain',50.52,58.9,46.72, 'C'),
('France',415.09,423.1,400.52, 'A') ],
columns = ['Desc', '2004', '2005', '2006', 'Tipo'],
index = [ 'EE', 'IE', 'EL', 'ES', 'FR'])
tabla3
Para acceder a las columnas, podemos usar la notación '.' o con la notación empleada en los diccionarios,
es decir, notación de corchetes y el nombre de la columna. Como podemos observar, el resultado es un
objeto de tipo Series:
tabla3.Desc
EE Estonia
IE Ireland
EL Greece
ES Spain
FR France
Name: Desc, dtype: object
tabla3['2004']
EE 8.40
IE 193.50
EL 1.40
ES 50.52
FR 415.09
Name: 2004, dtype: float64
Para acceder a partes de un DataFrame, conjunto de las, conjunto de columnas, etc., se puede utilizar la
misma notación empleada para los arrays, pero esa opción es poco e ciente en el caso de DataFrame.
Una alternativa mas e ciente es utilizar los indexadores loc e iloc. Veamos cada uno de ellos:
Permite recuperar partes del DataFrame indicando el nombre las de las y columnas que queremos
seleccionar, es decir, indicando la/las etiquetas de cada uno de los índices. Presentamos algunos
ejemplos que muestran el comportamiento. Para recuperar los datos de los países desde 'IE' hasta 'ES'
escribimos los siguiente:
tabla3.loc['IE': 'ES']
Si solo nos interesan la producción en los años 2004 y 2005 de los paises desde 'IE' hasta 'ES' escribimos
los siguiente:
2004 2005
IE 193.50 227.76
EL 1.40 1.60
ES 50.52 58.90
Para seleccionar los datos de producción de solo dos paises ('IE' y 'ES') en los años 2004 y 2006,
escribimos lo siguiente:
2004 2006
IE 193.50 221.05
ES 50.52 46.72
Para seleccionar un valor escalar, por ejemplo la producción de mantequilla en el año 2006 en el pais 'IE',
escribimos:
tabla3.loc['IE', '2006']
221.05
El indexador iloc permite recuperar partes del DataFrame indexando las y columnas con valores de
tipo int comenzando desde el 0. Para seleccionar la primera la del DataFrame escribimos lo siguiente:
tabla3.iloc[0]
Desc Estonia
2004 8.4
2005 7.7
2006 6.7
Tipo A
Name: EE, dtype: object
El resultado es un objeto de tipo Series. El nombre de la serie coincide con el valor del índice de la la
seleccionada. Para recuperar las tres primeras las y la primera y última columna, escribimos lo siguiente:
tabla3.iloc[:3,[0,-1]]
Desc Tipo
EE Estonia A
IE Ireland B
EL Greece A
tabla3.iloc[1,0]
'Ireland'
Otra forma de seleccionar partes de un dataframe es mediante el uso de ltros, de forma similar a cómo
se aplicaban en el caso de los arrays de Numpy.
tabla2
Valor
Murcia 10.7
Cantabria 7.5
Galicia 7.1
Melilla 17.8
Canarias 7.9
tabla2[tabla2 < 10]
Valor
Murcia NaN
Cantabria 7.5
Galicia 7.1
Melilla NaN
Canarias 7.9
Al aplicar el ltro, se mantienen los valores que cumplen el ltro y el resto se sustituyen por NaN.
En muchas ocasiones los datos que se están analizando no continen toda la información que
necesitamos para nuestro análisis, o símplemente no están en el formato apropiado. En estos casos,
Pandas ofrece la posibilidad de modi car los valores del DataFrame, borrar columnas, crear columnas
con nuevos valores o incluso columnas cuyos valores se calculan a partir de los valores de otras
columnas.
Para actualizar los datos de una columna podemos utilizar un valor concreto o un array de valores.
tabla3.Tipo = 'D'
tabla3
También podemos modi car el valor de una columna aplicando una función de transformación. Por
ejemplo, si queremos transformar los valores de la columna Desc a mayúsculas, escribimos lo siguiente:
tabla3
La función map aplica la función lambda a cada uno de los valores de la columna. Si queremos modi car
todos los datos de tipo numérico del DataFrame de forma que los datos se expresen con un único
decimal, usamos la función applymap junto con una función lambda como se muestra a continuación:
tabla3 = tabla3.applymap(lambda x : round(x,1) if type(x) == float else x)
tabla3
Aquí se le ve el sentido que tiene estudiar las funciones como argumementos de otras funciones, así
como las funciones lambda. Si quieres refrescar estos temas, repasa la Sección 2.4.
La operación pop de los DataFrame elimina la columna especi cada y devuelve dicha columna en un
objeto de tipo Series. Esta operación modi ca el DataFrame. Por otro lado, la operación drop permite
borrar tanto las como columnas, con la diferencia de que no devuelve los elementos borrados y no
modi ca el Dataframe, sino que devuelve una copia:
columna_tipo = tabla3.pop('Tipo')
columna_tipo
EE D
IE D
EL D
ES E
FR F
Name: Tipo, dtype: object
type(columna_tipo)
pandas.core.series.Series
tabla3
nuevo_df = tabla3.drop(['IE','FR'])
nuevo_df
tabla3
Para borrar columnas con drop, es necesario indicarlo mediante el argumento axis:
tabla3.columns
2004 2005
EE 8.4 7.7
IE 193.5 227.8
EL 1.4 1.6
ES 50.5 58.9
FR 415.1 423.1
tabla3
Para añadir nuevas columnas al DataFrame, ya sean calculadas o no, podemos usar la notación de
corchetes junto con el nombre de la nueva columna, al estilo de los diccionarios:
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python Data Analysis Library
Python for Data Analysis
Sección 4.3
Una de las características mas importante en torno a los índices de las estructuras de datos en Pandas es
el alineamiento. Pandas es capaz de alinear dos estructuras (series o dataframes) en base a las etiquetas
de sus índices. Esta propiedad es especialmente interesante cuando se realizan operaciones aritméticas,
teniendo en cuenta que dichas operaciones pueden involucar estructuras con etiquetas comunes en sus
índices, índices no ordenados, incluso puede darse el caso de tener etiquetas en solo uno de los índices.
En esta Sección mostramos algunos ejemplos de operaciones. En primer lugar cremos dos dataframes
con distintas etiquetas para sus índices.
import numpy as np
import pandas as pd
t1
Gastos Ingresos
Gastos Ingresos
Como podemos observar, ambos dataframes tienen algunas etiquetas comunes en sus índices y otras
que solo aparecen en uno de los dataframes. En el siguiente ejemplo, mostramos cómo se comporta la
operación suma aplicando el operador (+).
t1 + t2
Gastos Ingresos
Cuando una etiqueta aparece en el índice de ambos dataframes, se realiza la suma de los valores. En
caso contrario se asigna el valor NaN. Este es el comportamiento para todos los operadores aritméticos.
Sin embargo, como alternativa a ésta perdida de información, es posible utilizar los métodos add, sub,
div y mul, que implementan la operación suma, resta, división y multiplicación respectivamente. El
argumento fill_value permite indicar el valor por defecto para aquellas etiquetas que no aparecen en
el índice.
t1.add(t2, fill_value = 0 )
Gastos Ingresos
El comportamiento es análogo en el caso de operar con diferentes estructuras de datos, por ejemplo, con
un dataframe y una serie.
t1
Gastos Ingresos
s1
Gastos 100
Ingresos 200
dtype: int64
t1 - s1
Gastos Ingresos
Como se puede ver, el índice de la serie coincide con el índice de las columnas del dataframe. En ese
caso, los valores de la serie se restan a los valores del dataframe para cada columna donde existe
coincidencia. Si existe una etiqueta que no existe en alguna de las dos extructuras, el resultado de la
operación tendrá una nueva columna con dicha etiqueta cuyos valores serán NaN.
s2
Gastos 100
Ingresos 200
Nuevo 100
dtype: int64
t1 - s2
La ordenación de los datos por algún criterio es una operación muy habitual cuando se trata de analizar
datos. El método sort_index de los DataFrame ordena lexicográ camente los índices de un dataframes
en base a cualquiera de sus índices.
t1
Gastos Ingresos
t1.sort_index()
Gastos Ingresos
t1.sort_index(ascending = False)
Gastos Ingresos
Para ordenar el índice asociado a las columnas utilizamos el argumento axis con valor 1.
t1.sort_index(axis = 1, ascending = False)
Ingresos Gastos
Para ordenar los valores de una serie o un dataframe usamos el método sort_values. En el caso de los
dataframes, es necesario indicar las columnas (mediante una lista) por las que queremos ordenar en el
argumento by.
t1.sort_values(by = ['Gastos'])
Gastos Ingresos
Por defecto, la ordenación es ascendente en todas las columnas, pudiendo indicar otro orden mediante
una lista de valores de tipo bool en el argumento ascending.
Gastos Ingresos
Como resultado obtenemos un nuevo dataframe ordenado de forma ascendente en cuanto a la columna
Gastos y descendente en cuanto a la columna Ingresos.
Tratamiento de valores NaN
Resulta bastante habitual cuando se trabaja con datos, encontrarse con valores vacíos o desconocidos. Ya
hemos comentado anteriormente que estos valores se identi can con NaN(Not a Number). Aunque
Pandas es capaz de tratar este tipo de valores de forma implícita, resulta de mucha utilidad conocer las
herramientas que proporciona Pandas para su manejo.
Por un lado, es posible asignar valores NaN de forma explícita usando el valor np.nan de la librería
NumPy.
s3
A 1.0
B NaN
C 2.0
dtype: float64
Una de las opciones para eliminar masivamente estos valores, es usar el método dropna. El resultado es
una nueva estructura de datos sin valores NaN.
s3.dropna()
A 1.0
C 2.0
dtype: float64
s3
A 1.0
B NaN
C 2.0
dtype: float64
En el caso de dataframes, los valores NaN pueden estar distribuídos en varias columnas.
La operación dropna eliminará todas las las y todas las columnas que tengan algún valor NaN.
t3.dropna()
Para eliminar aquellas las en las que los valores de todas las columnas son NaN, usamos las opciones
axis y how con valores 0 y all respectivamente.
Y si queremos eliminar aquellas las o columnas que no alcanzan un número determinado de valores
distinto de NaN, usamos la opción thresh. Por ejemplo, para eliminar aquellas las que no tienen al
menos 3 valores distintos de Nan, escribiremos:
En los casos en los no deseamos eliminar datos con valores NaN, Pandas ofrece la posibilidad de sustituir
dichos valores por un valor por defecto. Para ello usamos el método fillna junto con el valor a sustituir.
t3.fillna(0)
0 0 ES 46449.0 0
1 Alemania 0 0.0 0
También es posible indicar un valor por defecto distinto para cada columna. En ese caso, usaremos un
diccionario como argumento del método fillna. Las claves del diccionario serán las etiquetas del índice
de las columnas afectadas y el valor asociado a cada una de las claves, será el valor por defecto que
sustituye a NaN.
d = {'País': 'ZZ', 'Código': 'ZZ', 'Pob.': 0}
t3.fillna(d)
0 ZZ ES 46449.0 NaN
En las operaciones estadísticas, Pandas ignora los valores NaN, pero si no queremos ignorarlos ha de
usarse el argumento skipna.
El siguiente dataframe contiene cierta información de tres países (código de país en el índice), en tres
ejercicios (columnas 2004, 2005 y 2006).
EE 8 7.0 6.0
IE 19 NaN 221.0
EL 1 1.0 NaN
También es posible calcular la media por columnas. Por defecto se ignoran los valores NaN, pero si no
queremos ignorarlos ha de usarse la opción skipna con valor False.
t4.mean(skipna=False)
2004 9.333333
2005 NaN
2006 NaN
dtype: float64
Entre las operaciones estadísticas más comunes se encuentran la correlación y la covarianza. Estas
operaciones se representan en Pandas mediante los métodos corr y cov respectivamente. Ambos
métodos se encuentran de nidos tanto para las Series como para los DataFrame. Así, es posible
calcular la correlación entre cualquier par de columnas de un dataframe, obteniéndose lo que se
denomina una matriz de correlación.
0 40 58 115
1 43 59 117
2 41 60 120
3 38 61 120
4 36 62 126
5 40 63 122
mcorr = t5.corr()
mcorr
De forma análoga, el método cov devuelve como resultado un nuevo dataframe que representa la matriz
de covarianza.
t5.cov()
De la misma forma, es posible calcular la correlación entre un par de columnas (correlación entre dos
series).
t5['Edad'].corr(t5['Altura'])
-0.7083002112022763
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python Data Analysis Library
Python for Data Analysis
Sección 5.1
Aunque otras librerías de Python proporcionan herramientas para importar y exportar datos, Pandas
aporta una gran potencia a la hora de leer y/o escribir cheros de datos. En esta sección abordamos el
tema de cómo leer y almacenar datos en diferentes formatos, ya sea en cheros o en bases de datos.
Pandas proporciona una amplia colección de funciones para realizar la lectura de datos como un objeto
de tipo DataFrame de una forma sencilla y exible.
import pandas as pd
import numpy as np
La función más utilizada en pandas para leer de forma exible cheros de texto plano es pd.read_csv.
La escritura de los datos contenidos en un dataframe o una serie se realiza mediante la operación
to_csv de las Series y DataFrame.
El chero animals1.csv contiene información del peso medio del cuerpo y cerebro de 8 especies de
animales:
animals1.csv
------------
Especie,Peso cuerpo,Peso cerebro
Big brown bat,0.023,0.3
Mouse,0.023,0.4
Ground squirrel,0.101,4
Tree shrew,0.104,2.5
Golden hamster,0.12,1
Mole,0.122,3
Galago,0.2,5
Rat,0.28,1.9
La función pd.read_csv permite crear un DataFrame a partir de los datos contenidos en un chero en
formato CSV.
dfcsv = pd.read_csv('datos/animals1.csv')
dfcsv
Como podemos ver, la creación de dataframes a partir de un chero CSV es bastante sencilla.
Cuando los datos almacenados en el chero no contienen la información del nombre de cada una de las
columnas, es decir, los datos aparecen en la primera la del chero (ver el chero animals2.csv),
podemos indicar mediante el argumento header la ausencia de cabeceras.
animals2.csv
------------
Big brown bat,0.023,0.3
Mouse,0.023,0.4
Ground squirrel,0.101,4
Tree shrew,0.104,2.5
Golden hamster,0.12,1
Mole,0.122,3
Galago,0.2,5
Rat,0.28,1.9
Las columnas del dataframe se indexan de forma automática con valores de tipo int comenzando por el
valor 0.
0 1 2
Usando el argumento names de la función pd.read_csv es posible asignar un nombre más signi cativo
a cada una de las columnas:
dfcsv = pd.read_csv('datos/animals2.csv',
header = None,
names = ['Especie', 'Peso Cuerpo', 'Peso cerebro'])
dfcsv
En algunas ocasiones las primeras líneas del chero no se corresponden con datos propiamente dichos,
si no que símplemente aportan una descripción de los datos que contiene (ver peliculas.csv).
peliculas.csv
------------
Cine de los 90
*********************************
Estreno: Fecha de estreno
Nombre: Título de la película
Cat: Género de la película
*********************************
Estreno,Nombre,Cat
1982,E.T. the ExtraTerrestrial,Fantasy
1982,Poltergeist,Horror
1992,Alien,Action
1992,The Crying Game,War
1995,Toy Story,Animation
1995,GoldenEye,Action
1995,Four Rooms,Thriller
El argumento skiprows permite omitir la lectura de un número determinado de las al comienzo del
chero.
También es posible omitir la lectura de ciertas las indicándolo mediante una lista de las en el
argumentos skiprows.
dfcsv = pd.read_csv('datos/peliculas.csv',
skiprows = [0, 1, 2, 3, 4 , 5, 6, 7, 8, 9])
dfcsv
Para crear variables de tipo DataFrame donde los valores de una de las columnas en el chero se van a
convertir en el índice del dataframe, usamos el argumento index_col:
dfcsv = pd.read_csv('datos/peliculas.csv', skiprows = 6,
index_col = ['Estreno'])
dfcsv
Nombre Cat
Estreno
También es posible crear dataframes donde el índice se compone de varias columnas del chero; se trata
de índices jerárquicos. Por ejemplo, podemos usar el argumento index_col con la lista de columnas del
chero que formará el índice del dataframe creado:
Cat
Estreno Nombre
Poltergeist Horror
GoldenEye Action
Los datos almacenados en las estructuras de datos de Pandas, ya sea en Series o DataFrames, pueden
ser almacenados en cheros. Esta es una operación se realiza de forma habitual después del
procesamiento de datos, limpieza, etc. Por ejemplo, para escribir los datos incluídos en un DataFrame en
un chero CSV utilizamos el método to_csv con el nombre del chero como argumento.
Nombre Minutos
Estreno
1982 Poltergeist 91
Admite una gran cantidad de parámetros. Opcionalmente podemos generar un chero csv con cabeceras
o sin ellas, con índices o sin ellos.
dfcsv.to_csv('./datos/datos.csv')
datos.csv
------------
Estreno,Nombre,Minutos
1982,E.T. the ExtraTerrestrial,114
1982,Poltergeist,91
1992,Alien,144
1992,The Crying Game,113
1995,Toy Story,81
1995,GoldenEye,130
1995,Four Rooms,94
Como se puede ver en el ejemplo, tanto el índice como las columnas del DataFrame se escriben en el
chero. Los argumentos index y header permiten cambiar el comportamiento por defecto:
datos.csv
------------
Nombre,Minutos
E.T. the ExtraTerrestrial,114
Poltergeist,91
Alien,144
The Crying Game,113
Toy Story,81
GoldenEye,130
Four Rooms,94
En éste punto nos preguntamos qué sucede con los valores NaN existentes en un dataframe. Por defecto,
los valores NaN se muestran como campos vacíos en el chero.
col = [ 1, 2, np.nan, 4, np.nan, np.nan, np.nan]
Estreno
datos.csv
------------
Nombre,Nueva Columna,Minutos
E.T. the ExtraTerrestrial,1.0,114
Poltergeist,2.0,91
Alien,,144
The Crying Game,4.0,113
Toy Story,,81
GoldenEye,,130
Four Rooms,,94
El argumento na_rep permite reemplazar los valores NaN del dataframe por otros que elija el usuario.
datos.csv
------------
Nombre,Nueva Columna,Minutos
E.T. the ExtraTerrestrial,1.0,114
Poltergeist,2.0,91
Alien,0,144
The Crying Game,4.0,113
Toy Story,0,81
GoldenEye,0,130
Four Rooms,0,94
Aunque los ejemplos anteriores solo tratan con dataframes, el comportamiento es análogo si tratamos
con series.
Otra forma muy habitual para guardar datos en forma tabular es mediante hojas de cálculo en un libro o
chero de Microsoft Excel. Pandas incluye la función pd.read_excel para realizar la lectura de datos y
crear dataframes, mientras que el método to_excel de los Dataframe permite escribir los datos en una
hoja de cálculo de Microsoft Excel.
La función pd.read_excel permite crear objetos de tipo DataFrame a partir de los datos contenidos en
cheros con extensión .xls y .xlsx. La siguiente gura muestra el contenido de dos hojas contenidas
en el chero clientes.xlsx.
Por defecto, la función pd.read_excel lee los datos de la primera hoja dentro del libro, pero se puede
indicar que lea los datos de otra hoja usando el argumento sheet_name.
dfexcel = pd.read_excel('./datos/clientes.xlsx')
dfexcel
3 CLI-0013 BRIGGITTE F
5 CLI-0020 MONICA F
La función pd.read_excel es tan exible como lo pueda ser la función pd.read_csv para cheros de
texto. Es posible saltar cabeceras, leer un número determinado de las, leer solo algunas de las columnas
contenidas en la hoja de cálculo, etc. Su funcionamiento varía dependiendo de los valores de los
argumentos cuando se invoca la función.
En el siguiente ejemplo aplicamos una función de conversión (función funtitle previamente de nida) a
los valores contenidos en la columna NOMCLI de la primera hoja del chero.
def funtitle(cadena):
return cadena.title()
3 CLI-0013 Briggitte F
5 CLI-0020 Monica F
dfexcel = pd.read_excel('./datos/clientes.xlsx',
converters = {'NOMCLI': lambda x: x.title() })
dfexcel
3 CLI-0013 Briggitte F
5 CLI-0020 Monica F
El argumento converters recibe como valor un diccionario, cuyas claves son las columnas sobre las que
se va a aplicar una función de conversión. El valor asociado a cada clave es la función a aplicar. Observa
que esta función se ha de nido como una función lambda.
dfexcel = pd.read_excel('./datos/clientes1.xlsx',
na_values = {'SEXO': ['Desconocido'],
'TLF' : ['-'] } )
dfexcel
El argumento na_values permite sustituir valores contenidos en la hoja de cálculo por NaN. Dicho
argumento recibe un diccionario, cuyas claves son las columnas sobre las que se va a aplicar la
sustitución, y el valor de cada clave es la lista de valores que han de ser sustituidos por NaN. Las celdas
vacías en la hoja de cálculo se transforman automáticamente en NaN en el dataframe.
La clase ExcelFile de Pandas tiene un mejor rendimiento cuando se trabaja con varias hojas de cálculo
de un chero Excel, ya que el chero se carga en memoria una sola vez. En el siguiente ejemplo,
mostramos cómo se lee un libro de Microsoft Excel mediante la función pd.ExcelFile. Posteriormente,
mediante el método to_excel creamos tantos dataframes como hojas de cálculo queramos leer.
libro = pd.ExcelFile('./datos/clientes.xlsx')
df1 = pd.read_excel(libro, sheet_name= 'Hoja1', index_col = [0])
df2 = pd.read_excel(libro, sheet_name= 'Hoja2')
df1
NOMCLI SEXO
CODCLI
CLI-0013 BRIGGITTE F
CLI-0020 MONICA F
df2
Pandas también ofrece la posibilidad de escribir los datos incluídos en un DataFrame en una hoja de un
chero Excel. Para ello usamos el método to_excel de los DataFrame indicando el nombre del chero.
Los argumentos header e index , discutidos en el caso del método to_csv, también están disponibles
aquí.
Se puede observar que los valores NAN del dataframe pasan a ser celdas vacías en la hoja de cálculo,
pudiéndose usar el argumento na_rep para cambiar el comportamiento.
En el siguiente ejemplo usamos el argumento sheet_name para asignar un nombre a la hoja de cálculo
creada entro del chero y el argumento columns para indicar el subconjunto de columnas que queremos
llevar a la hoja de cálculo.
El método to_excel tiene ciertas limitaciones. Por ejemplo, cuando queremos escribir los datos de
distintos dataframes en distintas hojas de cálculo dentro del mismo libro. La clase ExcelWriter de
Pandas resuelve éste problema.
En el siguiente ejemplo, mostramos cómo se crea un libro de Microsoft Excel mediante la función
pd.ExcelWriter. Posteriormente, mediante el método to_excel escribimos los datos de distintos
dataframes en distintas hojas, dentro del mismo chero.
df1 = dfexcel.loc[:, ['NOMCLI', 'SEXO']]
df1
NOMCLI SEXO
0 ROSA ELVIRA F
3 BRIGGITTE F
4 SAULO ANDRE M
5 NaN F
CODCLI TLF
0 CLI-0001 7578.0
1 CLI-0003 NaN
2 CLI-0004 NaN
3 CLI-0013 NaN
4 CLI-0018 NaN
5 CLI-0020 7600.0
libro = pd.ExcelWriter('clientes6.xlsx')
df1.to_excel(libro,'Datos en df1', index = False)
df2.to_excel(libro,'Datos en df2', index = False)
libro.save()
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python for Data Analysis
Sección 5.2
Hoy en día existen muchos datos alojados en sitios web. En algunos casos, los datos se encuentran en
cheros (CSV o Excel) y pueden ser descargados libremente para su uso posterior. En otros casos, los
datos están integrados en la propia página a través de su código HTML, por lo que resulta muy útil
disponer de herramientas capaces de navegar por dicho código en busca de los datos que nos interesen.
La librería Pandas proporciona herramientas e cientes para manejar documentos en formato HTML. El
método to_html de la clase DataFrame permite transformar dataframes en tablas HTML de forma
automática. La operación inversa también es posible; la función pd.read_html es capaz de recorrer el
código HTML de una página web en busca de los datos contenidos en tablas.
En el siguiente ejemplo, mostramos cómo extraer la información de la página web con URL
"https://es.wikipedia.org/wiki/Anexo:Municipios_de_la_Comunidad_de_Madrid". Esta página muestra
datos asociados a distintas localidades de la Comunidad de Madrid. Para poder acceder al código HTML
de una página web es necesario realizar una petición usando el protocolo HTTP Request/Response.
import requests
import pandas as pd
url = "https://es.wikipedia.org/wiki/Anexo:Municipios_de_la_Comunidad_de_Madrid"
respuesta = requests.get(url)
if respuesta.status_code == 200:
print('La petición HTTP ha ido bien')
else:
print('Problemas con la petición...')
La librería Requests permite realizar peticiones HTTP de forma muy sentilla. Para usarla es necesario
importarla. La función requests.get abre una conexión con el servidor donde se encuentra la url y
manda la petición. La respuesta del servidor se almacena en un objeto de la clase Response. El objeto
devuelto almacena muchos datos relacionados con la petición HTTP (cabeceras, cookies, etc.). De
momento nos interesa conocer si la petición ha tenido éxito (propiedad status_code) y el string que
representa el documento HTML de la página (propiedad text).
codigoHTML = respuesta.text
lista_dataframes = pd.read_html(codigoHTML, header=0)
La función pd.read_html devuelve una lista de dataframes, en el caso de que la página asociada a la url
contenga algún elemento de tipo <table>. En otro caso, la función no devuelve la lista vacía, sino que
devuelve un error de ejecución.
len(lista_dataframes)
lista_dataframes[0].head(7)
Como se puede observar, la función pd.read_html ignora todos los elementos contenidos en el
documento HTML que no tengan nada que ver con tablas HTML.
Una tabla HTML viene representada por una estructura jerárquica donde el elemento principal es el
elemento <table>. Cada la de la tabla se representa con un elemento de tipo <tr> y cada celda es un
elemento de tipo <th> para las celdas de cabecera y <td> para el resto. El siguiente ejemplo muestra el
código de una tabla HTML:
from IPython.display import HTML
s = """
<table>
<tr>
<th><strong>Ana</strong></th>
<th><strong>Pablo</strong></th>
<th><strong>Jaime</strong></th>
</tr>
<tr>
<td>10</td>
<td>20</td>
<td>30</td>
</tr>
<tr>
<td>40</td>
<td>50</td>
<td>60</td>
</tr>
</table>"""
HTML(s)
10 20 30
40 50 60
El método to_html de la clase DataFrame es capaza de crear una tabla HTML a partir de los datos
contenidos en el dataframe de forma automática.
Una vez generada la tabla HTML, podemos integrar el código en cualquier documento HTML para ser
interpretado por un navegador.
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python for Data Analysis
librería request
lxml
Sección 5.3
Otro de los estándar para el intercambio de datos en la Webes el formato JSON (JavaScript Object
Notation). Al igual que ocurría con XML, permite representar cualquier estructura de datos de una forma
jerárquica, la cual puede ser fácilmente representada en forma de árbol.
Pandas dispone de la función pd.read_json() para crear dataframes a partir de un documento JSON.
La operación recíproca, el método to_json() de la clase DataFrame que permite guardar dataframes
en formato JSON.
Para entender cómo navegar por este tipo de documentos es necesario conocer su estructura interna.
Para empezar, veremos cómo crear un documento en formato JSON a partir de un dataframe usando el
método to_json.
import pandas as pd
import json
tabla.to_json('peliculas.json')
El documento peliculas.json se muestra a continuación:
peliculas.json
------------------
{
"Cat": {
"3": "War",
"2": "Action",
"0": "Fantasy",
"1": "Horror"
},
"Nombre": {
"3": "The Crying Game",
"2": "Alien",
"0": "E.T. the ExtraTerrestrial",
"1": "Poltergeist"
},
"Extreno": {
"3": 1992,
"2": 1992,
"0": 1982,
"1": 1982
}
}
La función pd.read_json permite realizar la operación inversa. Dicha función recibe como argumento la
ruta del chero que contiene los datos en formato JSON.
tabla = pd.read_json('./peliculas.json')
tabla
En este caso, el proceso es muy simple porque el contenido del chero peliculas.json tenía una
estructura tabular, pero eso no es lo normal. La siguiente gura muestra el contenido del chero
canciones.json.
canciones.json
--------------
[
{
"Songs": [
{
"Length": "6:29",
"Date": 1992,
"Title": "Nothing Else Matters"
},
{
"Length": "4:21",
"Title": "Hero Of The Day"
}
],
"Genre": "Metal",
"Group": {
"Name": "Metallica"
}
},
{
"Songs": [
{
"Length": "4:37",
"Date": 2010,
"Title": "Need you now"
},
{
"Length": "4:17",
"Date": 2010,
"Title": "I Run To You"
},
{
"Length": "3:44",
"Title": "American honey"
}
],
"Genre": "Country pop",
"Group": {
"Name": "Lady Antebellum"
}
}
]
La función json_normalize de la sublibrería Pandas aporta un poco más de potencia. Por ejemplo, para
crear un dataframe con la información de las canciones, escribimo lo siguiente:
La función json_normalize recibe como argumentos el objeto de la clase json y la clave del documento
que contiene los datos que buscamos. Si además queremos añadir al dataframe la información del grupo
musical al que pertenece cada canción, tenemos que añadir un tercer argumento que indica la lista de
claves que queremos añadir.
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python for Data Analysis
librería request
lxml
Sección 5.4
En esta sección mostramos cómo interacciona Pandas con la base de datos relacional MySQL.
En Python existe un mecanismo estándar para el acceso a bases de datos relacionales. Mostramos el
procedimiento a seguir para realizar la conexión, lectura y escritura de datos desde Python. Este
procedimiento será similar para otros sistemas gestores de bases de datos, y lo único que podrá cambiar
será la cadena de conexión.
Para el caso de bases de datos SQL utilizaremos el módulo sqlalchemy que proporciona las
herramientas necesarias para gestionar varios tipos de bases de datos (lo que SQLAlchemy denomina
dialecto).
MySQL
A continuación mostramos cómo establecer la conexión con una base de datos MySQL. En primer lugar
tenemos que importar el método create_engine disponible en el módulo sqlalchemy. Dicho método
establece los parámetros del tipo de base de datos concreta con la queremos trabajar (MySQL,
PostgreSQL, Microsoft SQL Server, Oracle, etc.) y los parámetros de conexión (usuario, contraseña, host y
puerto, etc).
Además, en el caso de MySQL, necesitamos instalar el módulo mysql(no viene en Anaconda), para poder
realizar la conexión.
# parámetros de conexión
host_port='127.0.0.1:3306'
database='sakila'
user_pass='root' # user:pass
parametros ='mysql+mysqlconnector://'+ user_pass + '@' + host_port + '/' + database
parametros
'mysql+mysqlconnector://[email protected]:3306/sakila'
engineMySQL = create_engine(parametros)
Consultas
La creación de un dataframe a partir de los datos almacenados en una base de datos relacional es muy
simple utilizando las funciones que proporciona Pandas. La función read_sql permite ejecutar una
consulta SQL pasada como argumento. El resultado, será un objeto de la clase DataFrame, con tantas
columnas como atributos haya en la sentencia select y tantas tuplas como las devueltas por la
consulta.
En el siguiente ejemplo preguntamos acerca de las tablas existentes en la base de datos sakila.
query = 'SELECT table_name \
FROM information_schema.tables \
WHERE table_schema = "sakila";'
table_name
0 actor
1 address
2 category
3 city
4 country
5 customer
6 lm
7 lm_actor
8 lm_category
9 lm_text
10 sample_table2
11 tabla_nueva
column_name data_type
0 lm_id int
1 title varchar
2 description varchar
3 release_year varchar
4 language_id int
5 original_language_id int
6 rental_duration int
7 rental_rate varchar
8 length int
9 replacement_cost varchar
10 rating varchar
11 special_features varchar
12 last_update varchar
title name
Crear tablas
categoria.to_sql(name='tabla_nueva', con=engineMySQL,
if_exists = 'append', index=False)
El método to_sql recibe como argumento el nombre de la tabla de la base de datos donde se insertarán
las tuplas del dataframe df. El argumento if_exists permite indicar que las las ya existentes en la
tabla han de añadirse o reemplazarse.
Sentencias Insert
A partir del motor creamos un objeto de tipo conexión. Este objeto nos permitirá ejecutar consultas SQL
de todo tipo (insert, delete, drop, select, etc.) mediante la función execute.
connn = engineMySQL.connect() # creamos un objeto de tipo conexión para ejecutar una consulta ins
ert
connn.execute(consulta_insert, 'Drácula', 'Horror')
connn.close() # cerramosla conexión
Delete
connn = engineMySQL.connect() # creamos un objeto de tipo conexión para ejecutar una consulta ins
ert
connn.execute(consulta_delete)
connn.close()
Referencias
Python for Data Analysis
SQL conector
El módulo sqlAlchemy - The Python SQL Toolkit
SQL Server
Connecting to Microsoft Access
Sección 5.5
Ejercicios
El dataset BlackFriday.csv contiene 550.000 registros sobre las ventas realizadas en un comercio.
Cada registro contiene información del cliente (sexo, si está casado o no, edad, etc.) e información de la
compra que ha realizado en el comercio (importe de la compra, tipo de producto, etc.).
1.
import pandas as pd
2.
Calcula los primeros estadísticos mínimo, máximo, media y desviación típica del importe de compra
Imp_Compra.
3.
Selecciona las las de las compras realizadas por hombre solteros. Guarda el resultado de la selección
anterior en otro DataFrame llamado compras_solteros.
Selecciona a los usuarios que han realizado alguna compra con importe por debajo de la media.
Añadir una nueva columna al dataframe data llamada Imp+IVA que contenga el importe total de la
compra con IVA (Importe de la compra más el 21% de IVA).
Calcula el número de compras realizadas por mujeres y el número de compras realizadas por hombres.
10
Calcula la media del importe. Crear una nueva columna en el dataframe ventas llamada 'Tipo_compra'
cuyo valor sea 'Low' si el importe de la compra está por debajo de la media y 'High' si el valor de la
compra es mayor que la media. Usa la operación map sobre la columna ìmporte para aplicar una función
lambda que haga la transformación.
Sección 6.1
Combinar DataFrames
Cuando la información está distribuía en varios dataframes, antes de proceder al análisis es necesario
combinarlos para crear nuevos dataframes con toda la información necesaria. Existen varias
herramientas en Pandas para combinar dataframes. Veamos algunas de ellas junto con algunos ejemplos
ilustrativos de su uso.
La función pd.merge permite combinar las las de dos dataframes en función del valor de una o varias
columnas. Se trata de la operación join al estilo del álgebra relacional. Las columnas de combinación se
indican mediante una lista en el argumento on de la función. El argumento how de la función pd.merge
permite indicar el tipo de combinación que queremos hacer (left, right, outer o inner). El resultado
es un nuevo dataframe.
Supongamos que por un lado tenemos información de la cantidad de producción de ciertos productos en
distintos paises européos, y por otro lado la información de esos productos. Los datos se muestran en
dos dataframes t1 y t2.
import pandas as pd
0 8.4 Estonia A
1 6.7 Estonia B
2 227.0 Ireland A
3 58.9 Spain Z
t2 = pd.DataFrame( d2 )
t2
Descripción Producto
0 Leche A
1 Cereales B
2 Aceite C
Por ejemplo, la función pd.merge permite combinar los dataframes t1 y t2 usando como campos de
combinación la columna Producto. Veamos ejemplos con distintas opciones:
Por defecto la función pd.merge realiza la operación inner. La opción inner combina los dataframes
devolviendo únicamente aquellas las que tienen valores idénticos en las columnas que se comparan.
La opción left combina dos dataframes devolviendo aquellas las que tienen valores idénticos en las
columnas que se comparan para unir ambas tablas, y todas las las del dataframe de la izquierda, tengan
o no correspondencia con las las del dataframe de la derecha. Las que no tengan correspondencia se
rellenan con NaN.
En este caso, no existe el valor Z en la columna Producto del dataframe de la derecha, por lo que el valor
de la columna Descripción pasa a ser NaN.
Análogamente, la opción right combina dos dataframes devolviendo como resultado aquellas las que
tienen valores idénticos en las columnas que se comparan para unir ambas datframes, y todas las las
del dataframe de la derecha, tengan o no correspondencia con las las del dataframe de la izquierda. Las
que no tengan correspondencia se rellenan con NaN.
La opción outer combina los dos datframes devolviendo la unión de las las devueltas por la opción
left y right:
result = pd.merge(t1, t2, how='outer', on=['Producto'])
result
La función pd.merge es muy rica en funcionalidad y los resultados dependen del valor de sus
argumentos. Con lo visto anteriormente, podríamos suponer que para poder combinar dos dataframes
es necesario que las columnas de combinación se llamen igual. Veamos que esto no es así.
Descripción Id.Producto
0 Leche A
1 Cereales B
2 Aceite C
Usamos los argumentos left_on y right_on en lugar de on para indicar las columnas de combinación
de los dataframes de la izquierda y derecha respectivamente:
ES 92 89
AL 230 231
IT 201 193
CH 144 137
ES España 46449
AL Alemania 81197
JP Japón 127120
Si queremos combinar ambos dataframes usando el valor del índice de ambos, usaremos los argumentos
left_index y right_index como se muestra a continuación:
ES 92 89 España 46449.0
0 España ES 46449
1 Alemania AL 81197
2 Japón JP 127120
Cuando la combinación de dataframes se realiza en base a los índices, resulta más apropiado utilizar el
método join de los DataFrame. El único requisito para poder utilizar esta operación es que los
dataframes involucrados no tengan columnas con el mismo nombre.
t4.join(t5)
ES 92 89 España 46449.0
La función pd.concat de Pandas permite construir nuevos objetos de tipo Series o Dataframe en base
a uno de los ejes. Ya vimos las funciones hstack y vstack de nidas en la librería Numpy para apilar
arrays horizontal y verticalmente respectivamente. En el caso de Pandas es un poco más complicado, ya
que no podemos olvidar que tenemos etiquetas, tanto para las las como para las columnas.
Veamos algunos ejemplos que muestren el comportamiento de la función pd.concat, tanto para series
como para dataframes. Supongamos que tenemos dos series s1y s2 con índices disjuntos:
ES 89
AL 231
JP 336
dtype: int64
IT 201
CH 144
dtype: int64
En este caso, podemos usar la función pd.concat para apilar verticalmente tanto los índices como los
valores. Para ello usaremos el argumento axis con valor 0:
ES 89
AL 231
JP 336
IT 201
CH 144
dtype: int64
0 1
AL 231.0 NaN
CH NaN 144.0
ES 89.0 NaN
IT NaN 201.0
JP 336.0 NaN
Como podemos observar, en ambos casos se produce la unión de los índices, ya que la interseción de los
índices es vacía.
Pongamos ahora el caso de que la intersección de índice no sea vacía. De nimos una tercera serie s3, de
forma que las series s1y s3 tienen el índice común ES.
s3 = pd.Series( [100, 201 , 144],
index = [ 'ES', 'IT', 'CH'])
s3
ES 100
IT 201
CH 144
dtype: int64
0 1
AL 231.0 NaN
CH NaN 144.0
ES 89.0 100.0
IT NaN 201.0
JP 336.0 NaN
El siguiente código permite apilar horizontalmente añadiendo el nombre a las columnas. Para ello hemos
usado el argumento keys:
Col 1 Col 2
AL 231.0 NaN
CH NaN 144.0
ES 89.0 100.0
IT NaN 201.0
JP 336.0 NaN
Para el caso de objetos de tipo DataFrame la mecánica es similar. En el siguiente ejemplo creamos dos
dataframes df1 y df2 :
Cantidad País
ES 8.4 Estonia
IR 227.0 Irlanda
SP 58.9 Epaña
df2
Descripción Producto
ES Leche A
IR Cereales B
El argumento ignore_index permite apilar los dataframes ignorando el valor de los índices:
result = pd.concat([df1, df2] , axis = 0, ignore_index = True)
result
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python Data Analysis Library
Python for Data Analysis
Sección 6.2
Una gran parte del tiempo empleado en la tareas de análisis de datos se invierte en la preparación de los
mismos. En la mayoría de las ocasiones, nuestras aplicaciones y desarrollos necesitan que los datos
tengan un determinado formato. Sin embargo, los datos que se encuentran en cheros o en bases de
datos, no tienen por qué cumplir, a priori, nuestros requisitos, por lo que será necesario proceder a su
limpieza, transformación, procesamiento, etc.
Importación de librerías
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
Operación Pivot
La operación pivot permite construir un nuevo dataframe a partir de otro, de forma que los valores de
una columna pasen a ser nombres de columna del nuevo dataframe. Esta operación se usa
habitualmente cuando se trabaja con bases de datos relacionales y tiene la misma funcionalidad que en
otros lenguajes de análisis de datos. Lo veremos más claro con un ejemplo.
El siguiente dataframe recoge la información de varios pedidos.
datos = np.array([[101, 'p1', 2],
[101, 'p2', 3],
[101, 'p3', 4],
[102, 'p3', 1],
[102, 'p2', 2],
[102, 'p4', 5],
[103, 'p2', 1],
[103, 'p1', 3],
[103, 'p4', 2]])
pedidos = pd.DataFrame(datos,
columns = ['Número_pedido', 'Producto', 'Cantidad'])
pedidos
0 101 p1 2
1 101 p2 3
2 101 p3 4
3 102 p3 1
4 102 p2 2
5 102 p4 5
6 103 p2 1
7 103 p1 3
8 103 p4 2
Nótese que asociado a cada pedido tenemos varios productos, cada uno de ellos con la cantidad pedida.
El método pivot de los Dataframe permite girar los datos y se invoca con al menos tres argumentos. El
primer argumento index tiene como valor el nombre de la columna cuyos valores contituirán las
etiquetas del índice del nuevo dataframe. El segundo argumento columns es el nombre de la columna
cuyos valores constituirán las etiquetas del índice de las columnas del nuevo dataframe. Por último, el
tercer argumento values es el nombre de la columna cuyos valores pasan a ser los valores del nuevo
Dataframe.
pedidos.pivot(index = 'Número_pedido', columns = 'Producto', values = 'Cantidad')
Producto p1 p2 p3 p4
Número_pedido
101 2 3 4 NaN
102 NaN 2 1 5
103 3 1 NaN 2
El método pivot exige que no existan dos las con los mismos valores para las columnas asociadas a los
dos primeros argumentos (index y columns). En este ejemplo, las columnas Número_pedido y
Producto forman lo que en bases de datos relacionales llamamos clave primaria, es decir, no existen dos
las con el mismo número de pedido y el mismo producto.
0 2001 1 p1 100
1 2001 2 p1 50
2 2001 3 p2 20
3 2004 1 p3 100
4 2004 1 p1 100
5 2004 2 p1 200
6 2007 2 p1 50
7 2007 3 p1 40
prod.pivot_table(index = 'Ejercicio', columns = 'Producto', values = 'Kg.', aggfunc = sum)
Producto p1 p2 p3
Ejercicio
En el ejemplo anterior, se aplica la función suma (sum), para sumar la cantidad de producto producida en
cada ejercicio.
El argumento fill_value permite reemplazar los valores NaN por un valor por defecto.
Producto p1 p2 p3
Ejercicio
2001 150 20 0
2007 90 0 0
Eliminación de duplicados
La detección de datos duplicados en dataframes supone un gran esfuerzo cuando se habla de grandes
cantidades de datos.
El método duplicated de los DataFrame permite identi car las repetidas. Como resultado se obtiene
una serie cuyos valores son de tipo bool, cada uno de los cuales está asociado a una la. El valor True
indica que ya existe una la anterior en el dataframe con los mismos valores (la primera aparición no se
considera repetida). En caso contrario, el valor será False. Consideremos el siguiente dataframe con las
repetidas:
t = pd.DataFrame([['Africa', 11506],
['Africa', 11506],
['Antarctica', 5500],
['Antarctica', 5500],
['Africa', 11506],
['Banks', 23],
['Africa', 10000]], columns = ['Nombre', 'Millas cuadradas'])
t
0 Africa 11506
1 Africa 11506
2 Antarctica 5500
3 Antarctica 5500
4 Africa 11506
5 Banks 23
6 Africa 10000
t.duplicated()
0 False
1 True
2 False
3 True
4 True
5 False
6 False
dtype: bool
El resultado del método duplicated se puede utilizar como ltro para obtener un dataframe con las las
repetidas.
t[t.duplicated()]
1 Africa 11506
3 Antarctica 5500
4 Africa 11506
Para eliminar las las repetidas de un dataframe utilizamos el método drop_duplicates de los
DataFrame.
t.drop_duplicates()
0 Africa 11506
2 Antarctica 5500
5 Banks 23
6 Africa 10000
También nos puede interesar eliminar las si se repiten los valores de un subconjunto de columnas. En
ese caso es necesario indicar dicho subconjunto de columnas en el argumento subset.
t.drop_duplicates(subset = ['Nombre'])
0 Africa 11506
2 Antarctica 5500
5 Banks 23
Por defecto se mantiene la primera la y se elimina el resto de las repetidas. El argumento keep con
valor last permite eliminar todas las las repetidas excepto la última aparición.
3 Antarctica 5500
5 Banks 23
6 Africa 10000
Aplicación de funciones
Al estar Pandas construída sobre la librería Numpy, las funciones universales de nidas en Numpy se
extienden para adaptarse a las nuevas estructuras de datos presentes en Pandas. Se trata de funciones
que se aplican a cada uno de los valores de la estructura, ya sea un objeto de tipo ndarray, Series o
DataFrame.
datos = [ ( 8, 7, 6),
(2, 4, 16),
(10,20,30),
(4,2,5),
]
t = pd.DataFrame( datos,
columns = [ '2004', '2005', '2006'],
index = [ 'Estonia', 'Ireland', 'Greece', 'Spain'])
t
Estonia 8 7 6
Ireland 2 4 16
Greece 10 20 30
Spain 4 2 5
Por ejemplo, para aplicar la raiz cuadrada a cada valor de un dataframe, utilizamos la función np.sqrt de
Numpy.
np.sqrt(t)
Para calcular el cuadrado de cada uno de los vlores de un dataframe, utilizamos la función np.square de
Numpy.
np.square(t)
Estonia 64 49 36
Ireland 4 16 256
Spain 16 4 25
La aplicación de funciones a cada uno de los valores de una serie o un dataframe no se reduce a las
funciones universales de nidas en Numpy, si no que también es posible aplicar funciones de nidas por el
usuario. En el caso de objetos de tipo Series es necesario utilizar el método map para aplicar funciones
de usuario.
f1 = lambda x: x ** 2 - 2
EE 2
IR 4
GR 6
ES 8
dtype: int64
s.map(f1)
EE 2
IR 14
GR 34
ES 62
dtype: int64
t.applymap(f1)
Estonia 62 47 34
Ireland 2 14 254
Spain 14 2 23
La siguiente función de usuario multiplica por 10 aquellos valores menos que 100.
Se puede utilizar la aplicación de funciones para crear nuevas columnas en un dataframe en base a los
datos de otra columna.
t['Nueva'] = t['2004'].map(f2)
t
Estonia 8 7 6 80
Ireland 2 4 16 20
Greece 10 20 30 100
Spain 4 2 5 40
El método apply de los DataFrame permite aplicar funciones de usuario tanto a las como a columnas
de un dataframe.
A B C
P1 10 20 30
P2 40 10 60
P3 70 80 10
t.apply(f3)
A 110
B 100
C 90
dtype: int64
El resultado es una serie, donde las etiquetas del índice son las etiquetas del índice de las columnas del
dataframe. Cada uno de los valores de la serie es el resultado de aplicar la función de usuario a cada
columna. El argumento axis permite aplicar la función a cada una de las las.
t.apply(f3, axis = 1)
P1 50
P2 100
P3 150
dtype: int64
La función de usuario puede devolver una serie en lugar de un valor escalar. Esto permitirá la aplicación
de varias funciones simultáneamente.
def f4(x):
return pd.Series([sum(x) , min(x), f3(x) ], index = ['Suma', 'Mínimo', 'f3'])
t.apply(f4, axis = 1)
Suma Mínimo f3
P1 60 10 50
P2 110 10 100
P3 160 10 150
Como podemos observar en el ejemplo anterior, el resultado es un dataframe con tantas columnas como
valores haya en la serie devuelta por la función de usuario.
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python Data Analysis Library (http://pandas.pydata.org/)
Python for Data Analysis (http://shop.oreilly.com/product/0636920023784.do)
Sección 6.3
Después de realizar la importación de datos y procesarlos, una de las tareas más habituales es la
agrupación de dichos datos en base a alguna característica, para posteriormente realizar operaciones
sobre cada uno de los grupos obtenidos. Ésto se realiza habitualmente en un único paso gracias al
método groupby de los DataFrame. Se trata de una operación con funcionalidad similar a la sentencia
group by del lenguaje SQL.
La operación Groupby
Por un lado, los datos se dividen en grupos en base a una o más características. Por ejemplo, es posible
hacer grupos de las (axis = 0) o hacer grupos de columnas (axis = 1). Una vez creados los grupos, es
posible realizar alguna operación con cada grupo. Por ejemplo, operaciones de agregación. Se aplica una
función (prede nida o de usuario) a cada uno de los grupos. El resultado es un valor para cada uno de los
grupos. También es posible realizar operaciones de transformación. Éstas se aplican a cada uno de los
grupos y se obtiene como resultado una serie para cada grupo.
Veamos algunos ejemplos. En primer lugar de nimos un dataframe con datos numéricos y datos de tipo
cadena.
import numpy as np
import pandas as pd
Supongamos que nos interesa conocer el número de idiomas que se habla en cada país. Una forma de
resolverlo es hacer tantos grupos como países y posteriormente contar el número de tuplas de cada
grupo. En primer lugar invocaremos al método groupby con la columna País.
g = t.groupby(['País'])
g
El resultado de la operación groupby es un objeto de tipo Groupby. Los objetos de tipo Groupby tiene
una serie de propiedades, por ejemplo la propiedad groups.
g.groups
La propiedad ngroups indica el número de grupos en los que se ha dividido el dataframe original.
g.ngroups
6
El método size de los GroupBy devuelve como resultado una serie, donde el índice está formado por los
nombres de cada grupo y los valores asociados se corresponden con el tamaño del grupo.
g.size()
País
Andorra 1
Belgium 2
Burundi 2
Chile 1
China 2
Cuba 2
dtype: int64
Lo más habitual es realizar la operación de división en grupos y posteriormente aplicar alguna operación
de apregación. Por ejemplo, podemos aplicar la función de agregación count, que cuenta el número de
las del grupo.
t.groupby(['País']).Idioma.count()
País
Andorra 1
Belgium 2
Burundi 2
Chile 1
China 2
Cuba 2
Name: Idioma, dtype: int64
Para conocer el número de países en los que se habla un determinado idioma, será necesario agrupar los
datos por idioma y posteriormente aplicar la función de agregación count, lo que nos dará como
resultado el número de paises.
t.groupby(['Idioma']).País.count()
Idioma
Dutch 4
French 4
Greek 2
Name: País, dtype: int64
El resultado es una serie donde las etiquetas del índice son los valores del campo de agrupación.
Para continuar con los ejemplos, supongamos que tenemos un chero envios.csv con información de una
colección de envíos. De cada uno de ellos conocemos la fecha de entrega, la categoría, el importe, el peso
del paquete y un indicador de si es considerado urgente o no.
Supongamos que deseamos conocer la media de Importe para cada Categoría. En este caso, debemos
agrupar por la columna Categoria, seleccionar la columna Importe, y posteriormente aplicar el método
mean que calcula la media.
fact.groupby(['Categoria']).Importe.mean()
Categoria
G 3.220000
M 1.866667
P 1.625000
Name: Importe, dtype: float64
Si deseamos calcular la media del importe para cada categoría dependiendo de si es urgente o no,
escribimos lo siguiente:
fact.groupby(['Categoria', 'Urgente']).Importe.mean()
Categoria Urgente
G No 4.060000
Si 0.700000
M Si 1.866667
P No 0.600000
Si 2.650000
Name: Importe, dtype: float64
En este caso, tenemos que agrupar por los valores de las columnas Categoria y Urgente. Como
podemos observar, el resultado es una serie con índice jerárquico. La función unstack devuelve como
resultado un dataframe con índice jarárquico para el índice de las columnas.
fact.groupby(['Categoria', 'Urgente']).Importe.mean().unstack()
Urgente No Si
Categoria
G 4.06 0.700000
M NaN 1.866667
P 0.60 2.650000
Los objetos de tipo GroupBy permiten iteración, generando una secuencia de tuplas del tipo
(nombre_grupo, contenido).
g = fact.groupby(['Categoria', 'Urgente'])
('G', 'No')
('G', 'Si')
('M', 'Si')
('P', 'No')
('P', 'Si')
Los resultados nos indican que se han creado 5 grupos. El siguiente fragmento de código muestra el
contenido de cada uno de los grupos.
for (nombre_grupo, contenido) in fact.groupby(['Categoria', 'Urgente']):
print(contenido)
type(contenido)
pandas.core.frame.DataFrame
Para hacer cosas más creativas podemos usar funciones para agrupar datos. Por ejemplo, es posible
aplicar una función a cada uno de los valores del índice y agrupar los datos en base al valor devuelto por
la función.
fact
Supongamos que nos interesa conocer el importe total facturado en cada día de la semana. Es decir, el
importe facturado los lunes, los martes, etc. La función de usuario calcular_dia devuelve como
resultado el día de la semana (valor numérico entre 0 y 6) de una fecha. En primer lugar, importamos la
librería date.
Por ejemplo, el día 26 de Junio del año 2006 fué lunes. La función calcular_dia devuelve el valor 0.
calcular_dia(date(2006,6,26))
r = fact.groupby(calcular_dia)
r.ngroups
7
for (dia, contenido) in r:
print(dia)
print(contenido)
0
Categoria Importe Peso Urgente
2006-06-26 G 2.99 1.1 No
1
Categoria Importe Peso Urgente
2006-06-20 P 0.9 0.99 Si
2006-10-17 P 4.4 0.20 Si
2006-06-27 M 2.8 0.34 Si
2
Categoria Importe Peso Urgente
2006-06-21 P 0.4 0.21 No
3
Categoria Importe Peso Urgente
2006-12-14 P 0.8 0.12 No
2006-06-22 G 4.2 0.40 No
4
Categoria Importe Peso Urgente
2006-06-23 M 0.1 2.7 Si
5
Categoria Importe Peso Urgente
2006-06-24 M 2.7 1.5 Si
6
Categoria Importe Peso Urgente
2006-06-25 G 0.70 1.32 Si
2006-10-29 G 4.99 0.34 No
Para conocer el importe total de cada grupo, aplicamos la función de agregación sum a la columna
Importe de cada uno de los grupos.
fact.groupby(calcular_dia).Importe.sum()
0 2.99
1 8.10
2 0.40
3 5.00
4 0.10
5 2.70
6 5.69
Name: Importe, dtype: float64
Los valores por los que se desea agrupar, no siempre se correponden con valores en columnas del
dataframe. El argumento axis del método groupby permite indicar si la agrupación se realizará por
etiquetas en el índice (axis = 0), o por valores de columnas (axis = 1). Además, el argumento level
permite indicar el nivel de índice por el que se quiere agrupar.
El dataframe pedidos recoge información de codigo de pedido (etiquetas del índice de las las), el código
de producto, la cantidad de producto dentro de cada pedido y el precio.
Si queremos calcular el precio medio de cada pedido, escribimos lo siguiente:
101 2.0
102 1.5
103 3.0
Name: Precio, dtype: float64
Agrupamos a nivel de índice de las e indicamos el nivel. Posteriormente aplicamos la media (función
mean) a la columna Precio.
Funciones de agregación
Una vez que tenemos los datos divididos en grupos, las operaciones de agregación (mean, sum, count,
etc.) permiten realizar operaciones cuyo resultado es un único valor por grupo. También es posible
de nir funciones de usuario y utilizarlas una vez construidos los grupos. Para ello usamos la función agg.
Supongamos que queremos calcular el precio total de cada pedido menos el precio del producto más
barato.
pedidos
En primer lugar de nimos la función que realiza el cálculo deseado. La función total_desc recibe como
argumento una serie, calcula la suma de los valores (método sum), el mínimo valor (método min) y
devuelve la diferencia de ambos valores.
def total_desc(datos):
minimo = datos.min()
total = datos.sum() - minimo
return total
101 3.0
102 2.0
103 7.0
Name: Precio, dtype: float64
La función agg admite una lista de funciones de agregación como argumento. Como resultado
obtendremos un objeto de tipo DataFrame, con tantas columnas como funciones de agregación en la
lista.
resumen = pedidos.groupby(level = 0, axis = 0).Precio.agg([sum, total_desc])
resumen
sum total_desc
A diferencia de las funciones de agregación vistas en la sección anterior, el método transform de los
Groupgy permite aplicar una función a cada uno de los grupos, devolviendo como resultado una serie
con tantos valores como el tamaño del grupo (no un valor único por grupo).
El dataframe tiempos recoge medicciones del tiempo (en segundos) que tardan en ejecutarse varios
procesos en sus distintas versiones.
tiempos = pd.DataFrame(datos)
tiempos
0 A 0.0 20
1 B 0.1 20
2 A 1.1 30
3 A 2.3 40
4 B 2.1 50
5 A 3.0 60
6 B 3.1 70
Supongamos que queremos calcular la diferencia de tiempos de cada proceso con respecto al mejor
tiempo de dicho proceso. La función de usuario diferencia recibe una serie como argumento, calcula el
mínimo valor (método min) y devuelve la diferencia entre cada valor de la serie y el mínimo calculado. El
resultado es una serie.
def diferencia(x):
minimo = x.min()
return x - minimo
El método transform permite aplicar la función diferencia a cada uno de los grupos.
t = tiempos.groupby(['Proceso']).Segundos.transform(diferencia)
t
0 0
1 0
2 10
3 20
4 30
5 40
6 50
Name: Segundos, dtype: int64
tiempos.insert(3, 'Diferencia', t)
tiempos
0 A 0.0 20 0
1 B 0.1 20 0
2 A 1.1 30 10
3 A 2.3 40 20
4 B 2.1 50 30
5 A 3.0 60 40
6 B 3.1 70 50
Si queremos calcular la desviación de tiempos con respecto a la media de tiempos de cada proceso,
de nimos la función desviacion.
def desviacion(x):
media = x.mean()
return x - media
d = tiempos.groupby(['Proceso']).Segundos.transform( desviacion )
d
0 -17.500000
1 -26.666667
2 -7.500000
3 2.500000
4 3.333333
5 22.500000
6 23.333333
Name: Segundos, dtype: float64
tiempos.insert(4, 'Desv', d)
tiempos
0 A 0.0 20 0 -17.500000
1 B 0.1 20 0 -26.666667
2 A 1.1 30 10 -7.500000
3 A 2.3 40 20 2.500000
4 B 2.1 50 30 3.333333
5 A 3.0 60 40 22.500000
6 B 3.1 70 50 23.333333
El método apply es más general que cualquiera de los métodos agg o transform. Por ejemplo,
podemos calcular la suma de los tiempos de cada proceso de distintas formas.
tiempos.groupby(['Proceso']).Segundos.sum()
Proceso
A 150
B 140
Name: Segundos, dtype: int64
tiempos.groupby(['Proceso']).Segundos.agg(np.sum)
Proceso
A 150
B 140
Name: Segundos, dtype: int64
tiempos.groupby(['Proceso']).Segundos.apply(np.sum)
Proceso
A 150
B 140
Name: Segundos, dtype: int64
En los ejemplos anteriores, el resultado es el mismo tanto si aplicamos la función sum directamente,
como si aplicamos np.sum mediante agg o apply. En el siguiente ejemplo, mostramos que existen
diferencias.
Creamos un nuevo dataframe a partir de tiempos seleccionando las columnas Proceso y Segundos.
sub = tiempos.loc[:,['Proceso', 'Segundos']]
sub
Proceso Segundos
0 A 20
1 B 20
2 A 30
3 A 40
4 B 50
5 A 60
6 B 70
sub.groupby(['Proceso']).sum()
Segundos
Proceso
A 150
B 140
sub.groupby(['Proceso']).apply(np.sum)
Proceso Segundos
Proceso
A AAAA 150
B BBB 140
El método apply aplica la función np.sum a todas las columnas. La suma de los valores de la columna
Proceso es la concatenación de cadenas.
El siguiente ejemplo utiliza el método apply para obtener los dos procesos que emplean menos tiempo
en ejecutarse. Las funciones de agregación (tanto las prede nidas como las de nidas por el usuario)
transforman un grupo en un valor escalar. Pero en este caso, necesitamos aplicar una función que
transforme un grupo en un conjunto de las (dos por cada grupo, los dos procesos más rápidos).
def mejores_tiempos(datos):
ordenado = datos.sort_values(by = 'Segundos')
return ordenado[:2]
Aplicamos la función de usuario mejores_tiempos usando el método apply. El resultado es una serie
con índice jerárquico. El primer nivel de índice es el código del proceso, el el segundo nivel se
corresponde con el índice en el dataframe original.
sub.groupby('Proceso').apply(mejores_tiempos).Segundos
Proceso
A 0 20
2 30
B 1 20
4 50
Name: Segundos, dtype: int64
Referencias
Big Data. Análisis de datos con Python. Sarasa Cabezuelo, Antonio; García Ruiz, Yolanda Aditorial
Garceta. ISBN: 978-84-1622-883-6
Python Data Analysis Library (http://pandas.pydata.org/)
Python for Data Analysis (http://shop.oreilly.com/product/0636920023784.do)
Sección 6.4
Ejemplo
Datasets
Los siguientes datasets contienen datos históricos de combates de boxeo( 1993 - 2019 )
El dataset ./data/ ght_data.csv' contiene 5144 registros con información relacionada con combates de
boxeo. Cada registro hace referencia un combate, con información de los luchadores, tipo de combate,
resultado del combate, etc. A continuación describimos algunos de los campos más importantes:
Los pre jos R_ y B_ se re eren a los luchadores de las esquinas rojas y azules respectivamente.
Por otro lado, el dataset ./data/ ghter_details.csv contiene infomación de luchadores de boxeo, el
nombre, altura y el peso.
Ejercicio 1
Creación de dataframes:
Antes de realizar la lectura de los cheros, revisamos el separador de campos de cada uno de ellos.
Utilizamos el parámetro parse_dates en la función read_csv para indicar al intérprete de Python que
el tipo de la columna date debe ser de tipo datetime.
Usamos las funciones necesarias para visualizar información general de cada uno de los dataframes
(Número de registros, nombres de las columnas, valores distintos de nulo para cada una de las columnas,
el tipo de datos de cada una de las columnas, etc).
# Sol:
import pandas as pd
combates = pd.read_csv('./data/fight_data.csv', delimiter =';', parse_dates = ['date'])
luchadores = pd.read_csv('./data/fighter_details.csv')
combates.head()
R_ ghter B_ ghter win_by last_round last_round_time Format Referee date location Winner we
5 Rnd Chicago,
Henry Marlon Marc 2019- Henry
0 KO/TKO 3 4:51 (5-5-5- Illinois, Bant
Cejudo Moraes Goddard 06-08 Cejudo
5-5) USA
5 Rnd Chicago,
Valentina Jessica Robert 2019- Valentina Wom
1 KO/TKO 2 0:26 (5-5-5- Illinois,
Shevchenko Eye Madrigal 06-08 Shevchenko Flyw
5-5) USA
TKO - Chicago,
Tony Donald 3 Rnd Dan 2019- Tony
2 Doctor's 2 5:00 Illinois, Light
Ferguson Cerrone (5-5-5) Miragliotta 06-08 Ferguson
Stoppage USA
Chicago,
Jimmie Decision - 3 Rnd Kevin 2019-
3 Petr Yan 3 5:00 Illinois, Petr Yan Bant
Rivera Unanimous (5-5-5) MacDonald 06-08
USA
Chicago,
Blagoy Decision - 3 Rnd Dan 2019- Blagoy
4 Tai Tuivasa 3 5:00 Illinois, Heav
Ivanov Unanimous (5-5-5) Miragliotta 06-08 Ivanov
USA
combates.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5144 entries, 0 to 5143
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 R_fighter 5144 non-null object
1 B_fighter 5144 non-null object
2 win_by 5144 non-null object
3 last_round 5144 non-null int64
4 last_round_time 5144 non-null object
5 Format 5144 non-null object
6 Referee 5121 non-null object
7 date 5144 non-null datetime64[ns]
8 location 5144 non-null object
9 Winner 5061 non-null object
10 weight_class 5144 non-null object
11 title_bout 5144 non-null bool
dtypes: bool(1), datetime64[ns](1), int64(1), object(9)
memory usage: 447.2+ KB
luchadores.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3313 entries, 0 to 3312
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 fighter_name 3313 non-null object
1 Weight 3238 non-null object
2 Height 3050 non-null object
dtypes: object(3)
memory usage: 77.8+ KB
Ejercicio 2
75
Para eliminar de nitivamente los valores NaN, usamos la operación dropna con el parámetro inplace
con valor true. Así el dataframe tendrá solo valores distintos de NaN.
# Sol b)
luchadores.dropna( inplace = True)
luchadores.head()
luchadores.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 3044 entries, 0 to 3312
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 fighter_name 3044 non-null object
1 Weight 3044 non-null object
2 Height 3044 non-null object
dtypes: object(3)
memory usage: 95.1+ KB
Como se puede observar, el dataframe luchadores tiene ahora 3044 las y ningún valor NaN.
Ejercicio 3
Modi ca el DataFrame luchadores. Crea una nueva columna llamada Weight gr que represente el
peso del luchador en gramos. (1 libra son 453.59 gramos). Observa que antes de crear la columna, has de
eliminar las unidades (cuatro últimos caracteres de cada valor en la columna Weight, para así quedarte
con un string que representa un número. Una vez eliminada las unidades, convierte dicho string a tipo
float y posteriormente transforma a gramos.
# Sol
peso = luchadores.Weight.map(lambda x: float(x[:-4]))
luchadores['Weight gr' ]= peso * 453.59
luchadores.head()
Ejercicio 4
La columna location del dataframe combates recoge información del lugar donde se celebran los
combates. Este campo recoge la ciudad, el estado y el país.
Modi ca el DataFrame combates. Crear una nueva columna denominada Country con información
únicamente del nombre del país. Te servirá de inspiración observar el comportamiento del siguiente
fragmento de código:
Como puedes observar, el país siempre es el último elemento de la lista parts (posición -1 de la lista).
Para resolver este apartado, deberás crear una función que extraiga el país y aplicar dicha función a toda
la columna location mediante map.
# Sol a)
combates['Country'] = combates.location.map(lambda x : x.split(',')[-1])
combates.head()
R_ ghter B_ ghter win_by last_round last_round_time Format Referee date location Winner we
June Chicago,
Jimmie Decision - 3 Rnd Kevin
3 Petr Yan 3 5:00 08, Illinois, Petr Yan Banta
Rivera Unanimous (5-5-5) MacDonald
2019 USA
June Chicago,
Blagoy Decision - 3 Rnd Dan Blagoy
4 Tai Tuivasa 3 5:00 08, Illinois, Heavy
Ivanov Unanimous (5-5-5) Miragliotta Ivanov
2019 USA
Ejercicio 5
combates.groupby(['Referee', "weight_class"]).date.count()
Referee weight_class
Adam Cheadle Bantamweight 1
Flyweight 1
Lightweight 2
Adam Martinez Bantamweight 1
Featherweight 1
..
Yves Lavigne Welterweight 52
Women's Bantamweight 7
Women's Featherweight 1
Women's Flyweight 1
Women's Strawweight 4
Name: date, Length: 854, dtype: int64
Ejercicio 6
Muestra los datos de los combates ordenados por fecha en orden descendente.
# Sol:
ordenados = combates.sort_values("date", ascending = False)
ordenados.head(10)
R_ ghter B_ ghter win_by last_round last_round_time Format Referee date location Winner
5 Rnd Chicago,
Henry Marlon Marc 2019- Henry
0 KO/TKO 3 4:51 (5-5-5- Illinois,
Cejudo Moraes Goddard 06-08 Cejudo
5-5) USA
Chicago,
Karolina Alexa Decision - 3 Rnd Kevin 2019- Alexa
7 3 5:00 Illinois,
Kowalkiewicz Grasso Unanimous (5-5-5) MacDonald 06-08 Grasso
USA
5 Rnd Chicago,
Valentina Robert 2019- Valentina
1 Jessica Eye KO/TKO 2 0:26 (5-5-5- Illinois,
Shevchenko Madrigal 06-08 Shevchenko
5-5) USA
Chicago,
Katlyn Joanne Decision - 3 Rnd Dan 2019- Katlyn
12 3 5:00 Illinois,
Chookagian Calderwood Unanimous (5-5-5) Miragliotta 06-08 Chookagian
USA
Chicago,
Eddie Grigorii 3 Rnd Kevin 2019- Eddie
11 KO/TKO 2 4:47 Illinois,
Wineland Popov (5-5-5) MacDonald 06-08 Wineland
USA
Chicago,
Darren Decision - 3 Rnd Marc 2019- Darren
10 Bevon Lewis 3 5:00 Illinois,
Stewart Unanimous (5-5-5) Goddard 06-08 Stewart
USA
Chicago,
Ricardo Calvin 3 Rnd Dan 2019- Calvin
8 KO/TKO 1 4:06 Illinois,
Lamas Kattar (5-5-5) Miragliotta 06-08 Kattar
USA
Chicago,
Decision - 3 Rnd Robert 2019- Yan
9 Yan Xiaonan Angela Hill 3 5:00 Illinois,
Unanimous (5-5-5) Madrigal 06-08 Xiaonan
USA
Chicago,
Aljamain Pedro Decision - 3 Rnd Marc 2019- Aljamain
6 3 5:00 Illinois,
Sterling Munhoz Unanimous (5-5-5) Goddard 06-08 Sterling
USA
Chicago,
Tatiana Nina Decision - 3 Rnd Robert 2019- Tatiana
5 3 5:00 Illinois,
Suarez Ansaro Unanimous (5-5-5) Madrigal 06-08 Suarez
USA
Ejercicio 7
Muestra un listado de paises distintos donde se han celebrado combates. Usa unique.
array([' USA', ' Sweden', ' Brazil', ' Canada', ' Russia',
' United Kingdom', ' Czech Republic', ' Australia', ' China',
' Argentina', ' Germany', ' Singapore', ' Chile', ' Poland',
' Japan', ' Netherlands', ' Mexico', ' New Zealand', ' Croatia',
' South Korea', ' Ireland', ' Philippines',
' United Arab Emirates', ' Puerto Rico'], dtype=object)
Ejercicio 8
Deseamos conocer en cuántos combates ha sido victorioso el luchador de calzoncillo rojo. Observa que la
columna Winner informa del nombre del luchador que ha ganado el combate.
combates.head(2)
R_ ghter B_ ghter win_by last_round last_round_time Format Referee date location Winner weight_cla
Creamos un ltro con la condición de que el nombre del ganador sea el mismo que el nombre del
luchador del calzoncillo rojo. Posteriormente, aplicamos el ltro al dataframe usando [ ]:
# Sol:
filtro = combates.R_fighter == combates.Winner
ganador_rojo = combates[filtro]
print('Ganador rojo en: ', ganador_rojo.shape[0], ' combates')
Ejercicio 9
a) Genera un listado de combates que se hayan ganado por KO/TKO. La columna win_by informa cómo
se ha ganado el combate.
Creamos un ltro con la condición de que el valor de la columna win_by sea KO/TKO. Posteriormente,
aplicamos el ltro al dataframe usando [ ]:
# Sol a)
filtro = combates.win_by == 'KO/TKO'
ganador_por_ko = combates[filtro]
ganador_por_ko
R_ ghter B_ ghter win_by last_round last_round_time Format Referee date location Winner
5 Rnd Chicago,
Henry Marlon Marc 2019- Henry
0 KO/TKO 3 4:51 (5-5-5- Illinois,
Cejudo Moraes Goddard 06-08 Cejudo
5-5) USA
5 Rnd Chicago,
Valentina Robert 2019- Valentina
1 Jessica Eye KO/TKO 2 0:26 (5-5-5- Illinois,
Shevchenko Madrigal 06-08 Shevchenko
5-5) USA
Chicago,
Ricardo Calvin 3 Rnd Dan 2019- Calvin
8 KO/TKO 1 4:06 Illinois,
Lamas Kattar (5-5-5) Miragliotta 06-08 Kattar
USA
Chicago,
Eddie Grigorii 3 Rnd Kevin 2019- Eddie
11 KO/TKO 2 4:47 Illinois,
Wineland Popov (5-5-5) MacDonald 06-08 Wineland
USA
... ... ... ... ... ... ... ... ... ... ...
No Denver,
Orlando Robert John 1994- Orlando
5131 KO/TKO 1 2:50 Time Colorado,
Wiet Lucarelli McCarthy 03-11 Wiet
Limit USA
No Denver,
Johnny David John 1994- Johnny
5133 KO/TKO 1 12:13 Time Colorado,
Rhodes Levicki McCarthy 03-11 Rhodes
Limit USA
No Joao Denver,
Gerard Kevin 1993- Gerard
5139 KO/TKO 1 0:59 Time Alberto Colorado,
Gordeau Rosier 11-12 Gordeau
Limit Barreto USA
No Joao Denver,
Kevin Zane 1993- Kevin
5142 KO/TKO 1 4:20 Time Alberto Colorado,
Rosier Frazier 11-12 Rosier
Limit Barreto USA
No Joao Denver,
Gerard 1993- Gerard
5143 Teila Tuli KO/TKO 1 0:26 Time Alberto Colorado,
Gordeau 11-12 Gordeau
Limit Barreto USA
1647 rows × 12 columns
Ejercicio 10
Muestra un listado de los combates arbitrados por Robert Madrigal que han sigo ganados por
KO/TKO. En este caso creamos dos ltros y los aplicamos a la tabla mediante el operador conjunción (&):
filtro1 = combates.win_by == 'KO/TKO'
filtro2 = combates.Referee == 'Robert Madrigal'
ganador_por_ko_robert = combates[filtro1 & filtro2]
ganador_por_ko_robert
R_ ghter B_ ghter win_by last_round last_round_time Format Referee date location Winner weig
5 Rnd Chicago,
Valentina Jessica Robert 2019- Valentina Wome
1 KO/TKO 2 0:26 (5-5-5- Illinois,
Shevchenko Eye Madrigal 06-08 Shevchenko Flyweig
5-5) USA
Chicago,
Frankie Eddie 3 Rnd Robert 2016- Eddie
1387 KO/TKO 3 1:54 Illinois, Bantam
Saenz Wineland (5-5-5) Madrigal 07-23 Wineland
USA
Chicago,
Gian Tom 3 Rnd Robert 2015- Light
1823 KO/TKO 2 0:27 Illinois, Tom Lawlor
Villante Lawlor (5-5-5) Madrigal 07-25 Heavyw
USA
Chicago,
Eddie Yves 3 Rnd Robert 2014- Eddie
2589 KO/TKO 2 4:16 Illinois, Bantam
Wineland Jabouin (5-5-5) Madrigal 01-25 Wineland
USA
Chicago,
Nikita 3 Rnd Robert 2014- Nikita
2594 Walt Harris KO/TKO 1 0:25 Illinois, Heavyw
Krylov (5-5-5) Madrigal 01-25 Krylov
USA
Chicago,
Anthony Donald 3 Rnd Robert 2013- Anthony
2983 KO/TKO 1 2:35 Illinois, Lightw
Pettis Cerrone (5-5-5) Madrigal 01-26 Pettis
USA
Chicago,
Lavar 3 Rnd Robert 2012- Lavar
3322 Joey Beltran KO/TKO 1 4:24 Illinois, Heavyw
Johnson (5-5-5) Madrigal 01-28 Johnson
USA
Ejercicio 11
Lightweight 989
Welterweight 969
Middleweight 725
Heavyweight 507
Light Heavyweight 502
Featherweight 442
Bantamweight 379
Flyweight 187
Women's Strawweight 143
Women's Bantamweight 111
Open Weight 92
Women's Flyweight 50
Catch Weight 38
Women's Featherweight 10
Name: weight_class, dtype: int64
Ejercicio 12
Nos interesa construir un dataframe que reuna la información de los combates y el peso del ganador.
Usa la operación merge.
# Sol:
R_ ghter B_ ghter win_by last_round last_round_time Format Referee date location Winner weig
5 Rnd Chicago,
Henry Marlon Marc 2019- Henry
0 KO/TKO 3 4:51 (5-5-5- Illinois, Banta
Cejudo Moraes Goddard 06-08 Cejudo
5-5) USA
5 Rnd Brooklyn,
Henry TJ Kevin 2019- Henry
1 KO/TKO 1 0:32 (5-5-5- New York, Flywei
Cejudo Dillashaw MacDonald 01-19 Cejudo
5-5) USA
Los
5 Rnd
Demetrious Henry Decision - Mike 2018- Angeles, Henry
2 5 5:00 (5-5-5- Flywei
Johnson Cejudo Split Beltran 08-04 California, Cejudo
5-5)
USA
Detroit,
Henry Sergio Decision - 3 Rnd Keith 2017- Henry
3 3 5:00 Michigan, Flywei
Cejudo Pettis Unanimous (5-5-5) Peterson 12-02 Cejudo
USA
Edmonton,
Henry Wilson 3 Rnd 2017- Henry
4 KO/TKO 2 0:25 Jerin Valel Alberta, Flywei
Cejudo Reis (5-5-5) 09-09 Cejudo
Canada
Sección 6.5
Ejercicios
Pregunta 1
# Sol:
Pregunta 2.
La columna loan_status recoge el estado del préstamo bancario. Muestra los distintos estados
disponibles. Puedes usar la operación unique().
# SOl:
Pregunta 3.
La columna loan_amnt recoge el importe del préstamo. ¿Cuántos préstamos tienen un importe superior
a 25000?
# SOL:
Pregunta 4.
El tipo de interés asociado a los préstamos se recoge en la columna int_rate. Crear un nuevo
dataframe con la información de los préstamos cuyo tipo de interés sea inferior al 10%. Muestra las 10
primeras las del dataframe recién creado.
# SOL:
Pregunta 5.
La columna term indica el plazo del préstamo y la columna loan_amnt recoge el importe del préstamo.
Calcula el importe medio de los préstamos a 36 meses cuya nalidad es home. La nalidad del préstamo
se recoge en la columna purpose.
# SOL:
Pregunta 6.
La columna loan_amnt recoge el importe del préstamo, mientras que el tipo de interés asociado a los
préstamos se recoge en la columna int_rate. Ordenar los datos de los préstamos en orden creciente de
tipo de interés y decreciente de importe del préstamo.
# SOL:
Pregunta 7.
La columna home_ownership recoge información del tipo de propiedad del cliente. Modi ca el valor de
dicha columna para que recoja el valor Bajo Riesgo en caso de RENT, y el valor Alto Riesgo en caso
de MORTGAGE.
# SOL:
Pregunta 8.
Calcular el máximo salario de los clientes por grado de estudios y estado de residencia. La columna
grade indica el grado de estudios del solicitante del préstamo y la columna addr_state recoge
información del estado de residencia del cliente.
# SOL:
Pregunta 9.
La columna emp_title recoge información del puesto de trabajo de cliente. Crea un nuevo dataframe
con los datos de los clientes, donde los valores NaN en dicha columna se sustituyan por el valor Rest.
Guarda el nuevo dataframe en un chero csv utilizando el símbolo ; como separador de campos.
# SOL:
Pregunta 10.
Calcula el número de clientes con nivel de estudios A que han pedido un préstamo cuya nalidad es
vacation.
# SOL: