Transformacion de Datos con Numpy y Pandas - Carlos Alarcon
Contents
3. Transformacion de Datos con Numpy y Pandas - Carlos Alarcon¶
3.1. Funciones Pandas¶
3.2. NumPy¶
3.3. Pandas¶
3.4. Numpy Array¶
El array es el principal objeto de la librería. Representa datos de manera estructurada y se puede acceder a ellos a traves del indexado, a un dato específico o un grupo de muchos datos específicos. Las operaciones en Numpy son hasta 50 veces mas rapidas que en Python, a continuacion se presenta la creacion de arrays de diferetes dimenciones.
lista = [1,2,3,4,5,10]
array_lista = np.array(lista)
matriz = [[1,2,3],[2,3,4],[23,43,1]]
array_matriz = np.array(matriz)
#Output
Array List <class 'numpy.ndarray'>
Array Matrix <class 'numpy.ndarray'>
Si hacemos slicing de matrices simpre colocamos (nrow:mrow,ncol:ncol)
E.g.:
matriz = [[1,2,3],[2,3,4],[23,43,1]]
array_matriz[-2:,0]
#Outuput
array([2,23])
Si el slicing se realiza sobre un a matriz de 3 dimenciones el orden es diferente como se muestra a continuacion.
#Creamos x = 2 rows, y = 4 cols, z = 2
a_3d_array = np.array([[[1,2,3,4],[2,3,4,5]],[[43,21,56,7],[2,1,3,5]]])
#Slicing a_3d_array
#a_3d_array[0] ->[[1,2,3,4],[2,3,4,5]] Profundidad o Eje Z
#a_3d_array[0,1] -> [2,3,4,5] Altura Eje Y
#a_3d_array[0,1,3] -> 5 Ancho Eje X
a_3d_array[0:,0:,2:]
# 1. Tomar ambas matrices de 2X4 Eje Z
# 2. Tomar tanto rows como cols de ambas matrices Eje Z
# 3. Tomar de la col3 a col4 de ambas matrices Eje X
#Output
array([[[ 3, 4],
[ 4, 5]],
[[56, 7],
[ 3, 5]]])
3.5. Tipos de datos¶
Los arrays de NumPy solo pueden contener un tipo de dato, ya que esto es lo que le confiere las ventajas de la optimización de memoria. Manipulacion de tipos de datos
en Numpy:
# Array con dtype para definir el tipo de dato
arr = np.array([0,1,2,4,5], dtype = "float64")
# Astype es una alternativa cuando el array ya ha sido creado.
arr = arr.astype(np.float64) #Output: array([0., 1., 2., 4., 5.])
arr = arr.astype(np.bool_) #Output: array([False, True, True, True, True])
arr = arr.astype(np.string_) #Output: array([b'0.0', b'1.0', b'2.0', b'4.0', b'5.0'], dtype='|S32')
3.6. Dimensiones¶
Tipos de Dimenciones
Tensor 3 Dimenciones en Series de Tiempo |
---|
Tensor 4 Dimenciones Imagenes |
Alto: Alto de la Imagen, Ancho: Ancho de la Imagen, Ejemplos: Cada una de las Imagenes, Canales: Composición de Color de Cada Imagen (Grises,RGB, etc) |
A traves del comando .ndim
podremos saber en Numpy con que dimensiones estamos trabajando. El argumento ndmin
dentro de array establece la cantidad de dimensiones minimas que debe tener nuestro array: np.array([1,2,3], ndmin=3)
al momento de crearlo #Output: [[[1,2,3]]]
asi no las tenga.
Una alternativa mas completa a ndmin
es la funcion np.expand_dims(np.array([1,2,3]), axis=0 )
en la cual ademas de expandir la dimension podemos indicar el Eje a expandir. Para eliminar, comprimir o reducir dimensiones donde no hay datos usamos np.squeeze([[[1,2,3]]])
Output: [1,2,3]
.
3.7. Creando arrays¶
Rangos Podemos crear arrays en python con
range(start,stop,steps)
como se muestra a continuacion:
range(0,10) # Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#Igualmente lo podemos hacer con Numpy np.arange(start,stop,steps:
np.arange(0,10) # Output: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Crear arrays de Zeros
np.zeros((rows,cols))
onp.zeros(n)
y de Onesnp.ones((rows,cols))
Rangos definidos con Linspace
np.linspace(start,stop,n)
Donde start y stop determina el rango digamos de 1 a 100 y n se refiere a la cantidad, por ejemplo 10 numeros del 1 al 100.Generar una Matriz identidad
np.eye(n)
#Para n = 3
#Output matriz identidad:
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
Generar Numeros Alatorios:
#1. General un num aleatorio de 0 a 1
np.random.rand()
#2. General un array de n aletorios
#np.random.rand(n) para n = 4
np.random.rand(3)
array([0.75949945, 0.73539686, 0.27406198])
#3. General un matriz de n x m aletorios
np.random.rand(rows,cols)
#4. Generar un aleatorio para un rango definido.
np.random.randint(n,m)
#5. Matriz aleatorio con valores enteros definidos n-m.
np.random.randint(n,m,(rows,cols))
3.8. Shape y Reshape¶
Es muy importante saber manipular las formas de los datos.
#Shape
arr = np.random.randint(1,10,(3,2))
arr.shape
#Output (rows,cols) shape indica incluso las dimensiones de un tensor.
(3, 2)
# Reshape permite manipular la forma de los datos arr.reshape(rows,cols) a veces las API requieren formas especificas y por esto es muy util.
arr.reshape(1,6) = np.reshape(arr,(1,6))
array([[7, 2, 4, 3, 3, 4]])
#Reshape + tipo de estructura segun lenguage, donde 'C'se refiere al lenguage.
np.reshape(arr,(1,6), 'C')
# 'A' -> Segun mi sistema
# 'F' -> Segun Fortran
3.9. Funciones principales de NumPy¶
Nos permiten realizar realizar estadisticos sobre arreglos e incluso concatenarlos.
import numpy as np
arr = np.random.randint(1,20,10) #Crear vector de 10 numeros con rango de 1 a 20
matriz = arr.reshape(2,5) #Reshape a matriz de 2 X 5
matriz = np.array([[15, 5, 8, 10, 8],[12, 8, 7, 10, 4]])
#Output:
#array([[15, 5, 8, 10, 8],
# [12, 8, 7, 10, 4]])
# .max() o .min() permite hacer las mismas operaciones sobre arrays
matriz.max() #Numero mas grande del array matricial
matriz.max(1)# Numero por eje de rows -> array([15, 12])
matriz.max(0)# Numero por eje de cols -> array([15, 8, 8, 10, 8])
matriz.argmax()# Posicion num mas grande -> 0 donde 0 es 15
matriz.argmax(1)# Posicion num mas grande eje rows -> array([0, 0])
matriz.argmax(0)# Posicion num mas grande eje cols -> array([0, 1, 0, 0, 0])
# .ptp() calcula la diferencia entre el .max() y .min()
matriz.ptp() #Donde max = 15 min = 4 luego ptp() -> 11
matriz.ptp(1) #Donde eje rows max = 15 min = 5
#Donde eje rows max = 12 min = 4
#Output array([10, 8])
matriz.ptp(0) #Donde eje cols de restan y se obtienen valores absolutos
#Output array([ 3, 3, 1, 0, 4])
np.percentile(matriz,50)#Es la mediana o percentil 50
#Output: 8.0
matriz.sort() #Ordenal los datos de menor a mayor
np.median(matriz)#Output 8.0 es igual al percentil 50
np.median(matriz, axis=0) #Median en Eje cols -> array([ 4.5, 7.5, 8. , 10. , 13.5])
np.median(matriz, axis=1) #Median en Eje rows -> array([8., 8.])
np.std(matriz)# Desviacion estandar de los datos -> 3.067
np.var(matriz)# Varianza -> 9.409
np.mean(matriz)# Media -> 8.7
#Concatenar dos arrays como los que se muestran a continuacion:
array_a = np.array([[1,2],[3,4]])
array_b = np.array([5,6])
np.concatenate((array_a, array_b))# ValueError: all the input arrays
# must have same number of dimensions
#Concatenar al eje de Cols
np.concatenate((array_a, array_b), axis = 0)# ValueError: all the input arrays
# must have same number of dimensions
#Para solucionarlo modificamos las dimenciones de array_b
array_b = np.expand_dims(array_b, axis=0)
np.concatenate((array_a, array_b), axis = 0)
#Output: array([[1, 2],
# [3, 4],
# [5, 6]])
#Ahora para hacer la concatenacion en el axis = 1 o rows podemos
#transponer array_b.T o hacer un array_b.reshape(2,1) de no ser asi
# aparecera ValueError: all the input array dimensions for the concatenation
#axis must match exactly
np.concatenate((array_a, array_b.T), axis = 1)
3.10. Copy¶
Lo correcto antes de empezar a manipular un objeto array o dataframe es crear una copia asi podremos hacer exploracion de datos con seguridad que no estamos deformando los datos originales.
#Creamos un array de 1dim
array_a = np.arange(0,10)
#array_a se asigna a slice_array lo cual es un ERROR!
#por que aun es el mismo espacio en memoria en vez de ser uno nuevo.
slice_array = array_a[0:6]
slice_array
Output : array([0, 1, 2, 3, 4, 5])
#Modificamos una parte del slice creyendo que solo estamos
#modificando slice_array lo cual no es verdad.
slice_array[:]=0
slice_array
Output : array([0, 0, 0, 0, 0, 0])
#Si validamos array_a veremos que tambien fue modificado 😥
array_a
Output : array([0, 0, 0, 0, 0, 0, 6, 7, 8, 9])
Shiit!
La forma correcta es crear una copia como se muestra a continuacion:
array_a = np.arange(0,10)
array_copy=array_a.copy()
array_copy[:] = 10
array_copy
Output : array([10, 10, 10, 10, 10, 10, 10, 10, 10, 10])
Y si verificamos nuestro array inicial veremos que no ubieron modificaciones 😊
array_a
Output : array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
3.11. Condiciones¶
A traves de los condicionales podemos filtrar cualquier estructura de Datos a continuacion crearemos una y realizaremos diferentes condicionales:
import numpy as np
#Creamos un array 'arr' de tipo int8
arr = np.linspace(1,10,10,dtype='int8')
arr
Output : array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=int8)
#Entrega una lista booleana si condicionamos el array
arr > 5
Output : array([False, False, False, False, False, True, True, True,True,True])
#Si colocamos el condicional dentro de [condicional] obtendremos los datos
indices_cond = arr > 5
arr[indices_cond]
Output : array([ 6, 7, 8, 9, 10], dtype=int8)
#Incluso podemos colocar varios condicionales para un solo array
arr[(arr>5) & (arr<9)]
Output : array([6, 7, 8], dtype=int8)
Se pueden transformar los datos no solo a traves de slices sino tambien usando condicionales:
arr[arr > 5] = 100
arr
Output : array([ 1, 2, 3, 4, 5, 100, 100, 100, 100, 100], dtype=int8)
Luego de modificados podemos hacer nuevas consultas 🤠, esto se trata de creatividad.
arr[arr == 100]
Output : array([100, 100, 100, 100, 100], dtype=int8)
3.12. Operaciones¶
A diferencia de las listas en python, los arrays en Numpy nos permiten hacer todo tipo de operaciones matematicas. Por ejemplo si tratamos de hacer una operacion matematica con una lista es probale que no logremos el resultado esperado como se muestra a continuacion:
import numpy as np
#Creamos una lista con python
lista = [1,2]
#Python no entiende la operacion que quiero aplicar
lista * 2
Output : [1, 2, 1, 2]
Si realizamos operaciones con numpy el resultado sera diferente.
#Creamos un array con numpy y realizamos una copia🚀.
arr = np.arange(0,10)
arr_cp = arr.copy()
arr_cp * 2 # Puede ser cualquier tipo de Operacion Aritmetica
#y si hay un MathError no detiene el programa.
Output : array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
Realizar operaciones de algebra lineal✖️➕➖🟰
#Sumamos el array original con la copia del mismo
arr + arr_cp
Output : array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
#Usar matrices o tensores y operarlos
matriz = arr.reshape(2,5)
matriz_cp = matriz.copy()
#Sumamos original con la copia de la matriz
matriz + matriz_cp
Output : array([[ 0, 2, 4, 6, 8], [10, 12, 14, 16, 18]])
Si queremos calcular el producto punto solo debemos asegurarnos que los arrays tiene dimenciones iguales y que uno esta transpuesto:
#Transpuesta de matriz_cp
matriz_cp.T
Output : #Transpuesta de matriz_cp matriz_cp.T
Ahora si! hay varias formas de hacerlo:
#Producto punto lo cual es muy util en la
#ciencia de datos por ejemplo en proyecciones
np.matmul(matriz,matriz_cp.T)
#'matriz @ matriz_cp.T' es lo mismo que 'np.matmul(matriz,matriz_cp.T)'
Output : array([[ 30, 80], [ 80, 255]])
A continuacion: Como calcular la proyeccion de un vector sobre otro, Video, tener en cuenta que producto matricial no es lo mismo que producto escalar o entre dos vectores.
3.13. Series y DataFrames en Pandas¶
Pandas Series
Es como un array unidimensional de Numpy donde podemos: Buscar por indice y hacer Slicing, Operaciones aritmeticas sobre distintos tipos de datos
Pandas DataFrame
Es como las estructuras matriciales de Numpy donde podemos hacer busqueda por indices, columnas o filas.
Pandas Series
import pandas as pd
#Crear Series de los Jugadores del PSG
#Si no definimos el indice este iniciara desde 0
psg_players = pd.Series(['Navas','Mbappe','Neymar','Messi'],
index=[1,7,10,30]
)
psg_players
Output : 1 Navas 7 Mbappe 10 Neymar 30 Messi dtype: object
#Es posible crear el Series a traves de un diccionario
dict = {1:'Navas',7:'Mbappe',10:'Naymar',30:'Messi'}
#Ahora pasamos el dict a Series de pandas y obtenemos lo mismo
pd.Series(dict)
Output : 1 Navas 7 Mbappe 10 Naymar 30 Messi dtype: object
#Realizar un slicing el cual se fija en la cantidad
# de elementos mas no en el indice.
psg_players[0:3]
Output : 1 Navas 7 Mbappe 10 Neymar dtype: object
Pandas DataFrame
#Un DataFrame se puede definir como una super dict o JSON file
# el DataFrame al menos tiene 3 dimenciones como se muestra:
super_dict = {'Jugador':['Navas','Mbappe','Neymar','Messi'],
'Altura':[183.0,170.0,170.0,165.0],
'Goles':[2,200,200,200]}
df_players = pd.DataFrame(super_dict, index=[1,7,10,30])
df_players
Output :
index |
Jugador |
Altura |
Goles |
---|---|---|---|
1 |
Navas |
183.0 |
2 |
7 |
Mbappe |
170.0 |
200 |
10 |
Neymar |
170.0 |
200 |
30 |
Messi |
165.0 |
200 |
Igualmente podemos hacer consultas de columnas, indices y datos
#Obtener columnas del DataFrame
df_players.columns
Output : Index(['Jugador', 'Altura', 'Goles'], dtype='object')
3.14. Leer archivos CSV y JSON con Pandas¶
import pandas as pd
#Montamos un sistema local de archivos a colab
# en este caso el de google drive
from google.colab import drive
drive.mount('/content/drive')
#Permite cargar modulos de Python es un magic command
%reload_ext autoreload
#Reload all modules except those excluded by %aimport
%autoreload 2
Output : Drive already mounted at /content/drive
Asi cargamos archivos csv y JSON
#Cagar archivos csv
#A veces debemos usar el argumento 'sep' dependiendo
# el seperador del archivo csv.
#El arg 'header' permite definir que fila se debe tomar como header por defecto es igual a 0
#El arg 'names' se refiere a los nombres de las columnas.
df_countries = pd.read_csv('/content/drive/MyDrive/conda_data_science/data/raw/Metadata_Country.csv')
Es poco comun leer archivos JSON, normalmente es csv o sql donde sql se convierte primero a csv.
#Se puede realizar comunmente en datos estructurados como JSON
#Podemos convertirlo a Series con el arg typ='Series'
pd.read_json('/content/drive/MyDrive/conda_data_science/data/raw/students.json')
Output:
edad cm pais genero Q1 Q2
0 10 115 co M 5.0 7
1 9 110 mx F 10.0 9
2 13 130 co F 8.0 9
3 14 155 mx M NaN 8
4 12 125 mx M 7.0 8
5 11 120 ch M 8.0 8
6 12 125 ch F 3.0 9
3.15. Filtrado con loc y iloc¶
#Filtrado por indices
df_data[0:4]
Output :
edad cm pais genero Q1 Q2
0 10 115 co M 5.0 7
1 9 110 mx F 10.0 9
2 13 130 co F 8.0 9
3 14 155 mx M NaN 8
#Filtrado por Columnas df_data[[‘edad’,’cm’,’pais’]]
#Primero filtramos por indice y luego por Columna con loc #df_data.loc[0:4,’edad’]
#Primero filtramos por indice y luego por Columna con iloc df_data.iloc[0:4,:2]
Output:
edad cm
0 10 115
1 9 110
2 13 130
3 14 155
3.16. Agregar o eliminar datos con Pandas¶
#Eliminamos columna 'edad' usamos arg inplace=True para que se guarde.
df_data.drop('edad', axis=1, inplace=True)
df_data
Output :
cm pais genero Q1 Q2
0 115 co M 5.0 7
1 110 mx F 10.0 9
2 130 co F 8.0 9
3 155 mx M NaN 8
4 125 mx M 7.0 8
5 120 ch M 8.0 8
6 125 ch F 3.0 9
#Borrado de Filas
df_data.drop([0,1,2],axis=0, inplace=True)
df_data
Output :
cm pais genero Q1 Q2
3 155 mx M NaN 8
4 125 mx M 7.0 8
5 120 ch M 8.0 8
6 125 ch F 3.0 9
#Borrado de Rango de Filas
df_data.drop(range(4,6),axis=0, inplace=True)
df_data
Output :
cm pais genero Q1 Q2
3 155 mx M NaN 8
6 125 ch F 3.0 9
#Agregar Columnas
import numpy as np
df_data['New_column'] = np.nan
df_data
Output :
cm pais genero Q1 Q2 New_column
3 155 mx M NaN 8 NaN
6 125 ch F 3.0 9 NaN
#Agregar filas
df_data.append(df_data, ignore_index = True)
Output :
cm pais genero Q1 Q2 New_column
0 155 mx M NaN 8 NaN
1 125 ch F 3.0 9 NaN
2 155 mx M NaN 8 NaN
3 125 ch F 3.0 9 NaN
3.17. Manejo de datos nulos¶
import pandas as pd
import numpy as np
#Creamos un diccionario con valores nulos
dict = {'col1':[1,2,3,np.nan],
'col2':[4,np.nan,6,7],
'col3':['a','b','c',None]}
df = pd.DataFrame(dict)
df
Output :
col1 col2 col3
0 1.0 4.0 a
1 2.0 NaN b
2 3.0 6.0 c
3 NaN 7.0 None
#Nos indica los valores nulos del dataframe
df.isnull()*1 # Lo multiplicamos por 1 para que sea mas facil filtar
Output :
col1 col2 col3
0 0 0 0
1 0 1 0
2 0 0 0
3 1 0 1
Reemplazar los valores nulos String
#Rellenar valores nulos con la palabra 'Missing'
df.fillna('Missing')
Output :
col1 col2 col3
0 1.0 4.0 a
1 2.0 Missing b
2 3.0 6.0 c
3 Missing 7.0 Missing
Estadistico
#Rellena con la media de los valores por columna
df.fillna(df.mean())
Output :
col1 col2 col3
0 1.0 4.000000 a
1 2.0 5.666667 b
2 3.0 6.000000 c
3 2.0 7.000000 None
Interpolacion
#Interpola los datos como si se tratade de una serie
df.interpolate()
Output :
col1 col2 col3
0 1.0 4.0 a
1 2.0 5.0 b
2 3.0 6.0 c
3 3.0 7.0 None
Borrar Nulos
#Borra todas las filas con valores nulos
df.dropna()
#Igual pero se base en una columna especifica
df.dropna(subset=['col2'])
Output :
col1 col2 col3
0 1.0 4.0 a
2 3.0 6.0 c
3 NaN 7.0 None
3.18. Filtrado por condiciones¶
Cargamos un DataFrame en Google Coolab
edad |
cm |
pais |
genero |
Q1 |
Q2 |
|
---|---|---|---|---|---|---|
0 |
10 |
115 |
co |
M |
5.0 |
7 |
1 |
9 |
110 |
mx |
F |
10.0 |
9 |
2 |
13 |
130 |
co |
F |
8.0 |
9 |
3 |
14 |
155 |
mx |
M |
NaN |
8 |
4 |
12 |
125 |
mx |
M |
7.0 |
8 |
5 |
11 |
120 |
ch |
M |
8.0 |
8 |
6 |
12 |
125 |
ch |
F |
3.0 |
9 |
3.19. Funciones principales de Pandas¶
import pandas as pd
import numpy as np
#Funciones importantes en pandas
#Info nos describe los tipos de datos
df_students.info()
Output :
<class 'pandas.core.frame.DataFrame'>
Int64Index: 7 entries, 0 to 6
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 edad 7 non-null int64
1 cm 7 non-null int64
2 pais 7 non-null object
3 genero 7 non-null object
4 Q1 6 non-null float64
5 Q2 7 non-null int64
dtypes: float64(1), int64(3), object(2)
memory usage: 392.0+ bytes
#Nos muestra algunos estadisticos basicos
# Asi podemos entender la distribucion de los datos
df_students.describe()
Output :
edad cm Q1 Q2
count 7.000000 7.000000 6.000000 7.000000
mean 11.571429 125.714286 6.833333 8.285714
std 1.718249 14.556949 2.483277 0.755929
min 9.000000 110.000000 3.000000 7.000000
25% 10.500000 117.500000 5.500000 8.000000
50% 12.000000 125.000000 7.500000 8.000000
75% 12.500000 127.500000 8.000000 9.000000
max 14.000000 155.000000 10.000000 9.000000
#La cantidad de memoria en bits que esta usando el DataFrame
df_students.memory_usage(deep=True)
Output :
Index 56
edad 56
cm 56
pais 413
genero 406
Q1 56
Q2 56
dtype: int64
#Cuenta el total de cada categoria en una columna
df_students['pais'].value_counts()
Output :
mx 3
co 2
ch 2
Name: pais, dtype: int64
#Repetimos una de las filas intensionalmente
#para usar la funcion borrar duplicados
#Añadimos row0 al final
df_students = df_students.append(df_students.iloc[0])
df_students.drop_duplicates(keep='last')#Mantiene ultimo Duplicado
Output :
edad cm pais genero Q1 Q2
1 9 110 mx F 10.0 9
2 13 130 co F 8.0 9
3 14 155 mx M NaN 8
4 12 125 mx M 7.0 8
5 11 120 ch M 8.0 8
6 12 125 ch F 3.0 9
0 10 115 co M 5.0 7
#Permite ordenar el Dataframe segun una columna
df_students.sort_values('cm',ascending=False)#Mayor a Menor
Output :
edad cm pais genero Q1 Q2
3 14 155 mx M NaN 8
2 13 130 co F 8.0 9
4 12 125 mx M 7.0 8
6 12 125 ch F 3.0 9
5 11 120 ch M 8.0 8
0 10 115 co M 5.0 7
0 10 115 co M 5.0 7
0 10 115 co M 5.0 7
1 9 110 mx F 10.0 9
3.20. Groupby¶
#Agrupamos por 'pais' el cual es una columna categorica
#Y realizamos el conteo por categoria de todo el dataframe
df_students.groupby('pais').count()
Output:
edad cm genero Q1 Q2
pais
ch 2 2 2 2 2
co 2 2 2 2 2
mx 3 3 3 2 3
alores maximos por categoria
#Podemos usar todo tipo de funciones de Agregacion
df_students.groupby('pais').max()
Output:
edad cm genero Q1 Q2
pais
ch 12 125 M 8.0 9
co 13 130 M 8.0 9
mx 14 155 M 10.0 9
#Indicar varias funciones
df_students.groupby('pais').agg([min,max])
Output:
#Funciones de Agregacion a traves de diccionario
df_students.groupby(['pais','genero']).agg({'edad':[min,max],'cm':sum})
Output:
3.21. Combinando DataFrames¶
Merge-Join Pandas Concat Axis 0 Pandas Concat Axis 1 Pandas
3.22. Merge y Concat¶
Concat
#Creamos 2 DataFrames para realizar operaciones entre conjuntos
df1 = pd.DataFrame({'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3'],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
df2 = pd.DataFrame({'A':['A4','A5','A6','A7'],
'B':['B4','B5','B6','B7'],
'C':['C4','C5','C6','C7'],
'D':['D4','D5','D6','D7']})
#Concatenacion por axis=0 o Filas
#Los indices se heredan por eso usamos ignore_index
concat_axis_0 = pd.concat([df1,df2], axis=0, ignore_index=True)
concat_axis_0
#Usualmente se concatena por axis 0, pero tambien se puede
#hacer a nivel de columnas o axis = 1
concat_axis_1 = pd.concat([df1,df2], axis=1, ignore_index=True)
concat_axis_1
Merge
#Merge Pandas funciona como un inner join por default, creamos dos DataFrames
left = pd.DataFrame({'key':['k0','k1','k2','k3'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']})
right = pd.DataFrame({'key':['k0','k1','k2','k3'],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
#El merge se realiza sobre una columna especifica como key
left.merge(right, on='key')
#Merge Pandas, modificamos DataFrames
left = pd.DataFrame({'key':['k0','k1','k2','k3'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']})
#Intencionalmente cambiamos el key a key2
right = pd.DataFrame({'key2':['k0','k1','k2','k3'],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
#El merge solo funcionara si se especifican las columnas
#de cada df por separado
left.merge(right, left_on='key', right_on='key2')
#Merge Pandas, creamos dos DataFrames
left = pd.DataFrame({'key':['k0','k1','k2','k3'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']})
#Intencionalmente cambiamos el key a key2 y k3 a nan
right = pd.DataFrame({'key2':['k0','k1','k2',np.nan],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
#El merge solo funcionara para los k0,k1,k2 no para k3
#k3 ya no hace parte de la coincidencias
left.merge(right, left_on='key', right_on='key2')
#Merge como un left join, dando prioridad a left
left = pd.DataFrame({'key':['k0','k1','k2','k3'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']})
#Intencionalmente cambiamos el key a key2 y k3 a nan
right = pd.DataFrame({'key2':['k0','k1','k2',np.nan],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
#Asi se veria un left joint usando 'how'
#Esto seria igual si lo hacemos con right join
left.merge(right, left_on='key', right_on='key2', how='left')
Output:
Join
#Join a diferencia de merge se basa en la filas
#creamos un df_left y df_right y le asignamos un indice
df_left = pd.DataFrame({'A':['A0','A1','A2'],
'B':['B0','B1','B2']}, index=['k0','k1','k2'],)
#Intencionalmente saltamos el indice k1 de df_left
df_right = pd.DataFrame({'C':['C0','C1','C2'],
'D':['D0','D1','D2']}, index=['k0','k2','k3'])
#Se realiza left join por default
df_left.join(df_right)'
Output:
#Podemos realizar diferentes tipos de joins a traves de 'how'
df_left.join(df_right, how='inner')
#how ='left'
#how ='outer'
#how ='inner'
#how ='right'
3.23. Pivot y Melt¶
Pivot, básicamente, transforma los valores de determinadas columnas o filas en los índices de un nuevo DataFrame, y la intersección de estos es el valor resultante.
#Pivot table es como una table dinamica en excel, solo tenemos que indicar
#cuales son las filas, columnas y de donde vienen los datos
df_students.pivot_table(index='pais',columns='genero',values='edad')
#Melt convierte columnas a filas
df_students[['pais','genero']].melt()
3.24. Apply¶
#Creamos una funcion de Python
def two_times(values):
return values * 2
#Utilizamos 'apply' y asi todos los valores de 'cm'
# se pasan por la funcion 'two_times'
#Crear un nueva columna con el calculo realizado
df_students['cm'].apply(two_times)
Output:
0 230
1 220
2 260
3 310
4 250
5 240
6 250
Name: cm, dtype: int64
#Lambdas con apply
df_students['lamda_1'] = df_students['cm'].apply(lambda x : x * 3)
df_students
#Lambda en todo el DataFrame
df_students['lamda_2'] = df_students.apply(lambda x : x['cm'] * 2 if x['genero'] == 'M' else x['cm'], axis=1)
df_students
Output: