Curso de introducción a la programación con Python¶

    Autor: Luis Fernando Apáez Álvarez
    -Curso PyM-
    Análisis Exploratorio de Datos
    Fecha: 29 de Noviembre del 2022

En esta clase veremos algunos elementos descriptivos, numéricos y gráficos entorno a un conjunto de datos. El proceso de análisis que realizaremos se conoce como análisis exploratorio de datos, el cual corresponde a lo primero que debemos de hacer de manera propia cuando trabajamos con un conjunto de datos y es de nuestro interés llevar acabo un análisis sobre éstos.

Para ello, comenzaremos por cargar los datos:

In [ ]:
import pandas as pd
df = pd.read_csv('https://cursopypagina.github.io/CursoPy/titanic.csv')

# vemos los primeros registros de nuestro conjunto de datos
df.head()
Out[ ]:
survived pclass age sibsp parch fare male age_was_missing embarked_from_cherbourg embarked_from_queenstown embarked_from_southampton
0 0 3 22.0 1 0 7.2500 1 False 0 0 1
1 1 1 38.0 1 0 71.2833 0 False 1 0 0
2 1 3 26.0 0 0 7.9250 0 False 0 0 1
3 1 1 35.0 1 0 53.1000 0 False 0 0 1
4 0 3 35.0 0 0 8.0500 1 False 0 0 1

Diremos que una variable (o dato) es cuantitativo si sus posibles valores son numéricos. En cambio, diremos que una variable es cualitativa si sus valores representan una cualidad, un atributo o una categoría. A las variables cualitativas también se le denominan categóricas.

De nuestro conjunto de datos anterior veamos cuales variables (columnas) son cuantitativas o cualitativas

In [ ]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 11 columns):
 #   Column                     Non-Null Count  Dtype  
---  ------                     --------------  -----  
 0   survived                   891 non-null    int64  
 1   pclass                     891 non-null    int64  
 2   age                        891 non-null    float64
 3   sibsp                      891 non-null    int64  
 4   parch                      891 non-null    int64  
 5   fare                       891 non-null    float64
 6   male                       891 non-null    int64  
 7   age_was_missing            891 non-null    bool   
 8   embarked_from_cherbourg    891 non-null    int64  
 9   embarked_from_queenstown   891 non-null    int64  
 10  embarked_from_southampton  891 non-null    int64  
dtypes: bool(1), float64(2), int64(8)
memory usage: 70.6 KB

Tenemos que las variables cuantitativas son age, fare y las cualitativas son el resto. Además, tendremos que las variables cuantitativas pueden ser discretas o continuas. De nuestro ejemplo tenemos que las variables cuantitativas son continuas.

Por otro lado, podemos ver también algunas estadísticas sobre las columnas numéricas de nuestro conjunto de datos

In [ ]:
df.describe()
Out[ ]:
survived pclass age sibsp parch fare male embarked_from_cherbourg embarked_from_queenstown embarked_from_southampton
count 891.000000 891.000000 891.000000 891.000000 891.000000 891.000000 891.000000 891.000000 891.000000 891.000000
mean 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208 0.647587 0.188552 0.086420 0.722783
std 0.486592 0.836071 13.002015 1.102743 0.806057 49.693429 0.477990 0.391372 0.281141 0.447876
min 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 0.000000 2.000000 22.000000 0.000000 0.000000 7.910400 0.000000 0.000000 0.000000 0.000000
50% 0.000000 3.000000 29.699118 0.000000 0.000000 14.454200 1.000000 0.000000 0.000000 1.000000
75% 1.000000 3.000000 35.000000 1.000000 0.000000 31.000000 1.000000 0.000000 0.000000 1.000000
max 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200 1.000000 1.000000 1.000000 1.000000

donde vemos:

  • El total de filas o registros: count=891
  • El promedio de los valores en dicha columna: mean
  • El valor mínimo registrado en dicha columna: min
  • El valor máximo registrado en dicha columna: max
  • La desviación estándar: std

Además, con base en la tabla anterior podemos comenzar a sacar información relevante sobre nuestros datos. Por ejemplo, si consideramos

In [ ]:
# guardamos las estadisticas obtenidas en la variable df_des
df_des = df.describe()
# nos concentramos solo en la columna survived
df_des['survived']
Out[ ]:
count    891.000000
mean       0.383838
std        0.486592
min        0.000000
25%        0.000000
50%        0.000000
75%        1.000000
max        1.000000
Name: survived, dtype: float64

Dado en dicha columna solo hay ceros y unos, que el promedio sea igual a 0.38 nos dice que, en promedio, hubo más decesos que personas que sobrevivieron.

¿Qué se puede decir de la columna age?

In [ ]:
# muestra el dataframe df_des pero solo la columna age
df_des['age']
Out[ ]:
count    891.000000
mean      29.699118
std       13.002015
min        0.420000
25%       22.000000
50%       29.699118
75%       35.000000
max       80.000000
Name: age, dtype: float64

Reesponde a la pregunta que se ha hecho anteriormente:

  • En promedio los pasajeros tenían 29 años

  • Edad del pasajero más joven: 0.4

  • Edad del pasajero más grande: 80



¿Qué se puede decir de la columna male?

In [ ]:
# muestra el dataframe df_des pero solo la columna male
df_des['male']
Out[ ]:
count    891.000000
mean       0.647587
std        0.477990
min        0.000000
25%        0.000000
50%        1.000000
75%        1.000000
max        1.000000
Name: male, dtype: float64

Reesponde a la pregunta que se ha hecho anteriormente:


  • Había más hombres que mujeres

Continuando, diremos que la frecuencia de una clase o una categoría es el número de veces que la clase es observada. De nuestro ejemplo, particularmente estaremos interesados en la columna survived y male, las cuales son variables categóricas con dos clases. Para visualizar las frecuencias de las clases utilizaremos un histograma:

In [ ]:
import matplotlib.pyplot as plt
plt.style.use('seaborn')

# veremos el histograma de las frecuencia de la variable survived
df.survived.hist(color='#01B9FA')
plt.title('Frecuencia para la variable survived', size=16)
plt.xlabel('0: deceso,  1: sobrevivió')
plt.ylabel('Frecuencia')
plt.show()

donde, tenemos que la clase 0 (decesos) tiene más frecuencia de aparición que la clase 1 (sobrevivientes).


Gráfica un histograma como el anterior para la columna male y obtén conclusiones (qué observas).

In [ ]:
# Escribe tu codigo aqui
# veremos el histograma de las frecuencia de la variable survived
df.male.hist()
plt.title('Frecuencia para la variable male', size=16)
plt.ylabel('Frecuencia')
plt.show()
  • Casi 600 hombres, un poco más de 300 mujeres

Por otro lado, puede que estemos interesados en ver un gráfico informativo para la columna age, la cual no es categórica por lo cual no podremos ver un histograma.

Para poder ver un histograma con la información de dicha columna emplearemos:

In [ ]:
# Consideramos solo la columna edad
df_age = pd.DataFrame(df.age)
df_age
Out[ ]:
age
0 22.000000
1 38.000000
2 26.000000
3 35.000000
4 35.000000
... ...
886 27.000000
887 19.000000
888 29.699118
889 26.000000
890 32.000000

891 rows × 1 columns

In [ ]:
# Definimos una funcion para clasificar a las personas de acuerdo a
# un rango de edad
def rango_edad(x):
  if x <= 12:
    return 'Niño/a'
  elif (x > 12) and (x <= 18):
    return 'Joven'
  elif (x > 18) and (x <= 30):
    return 'Adulto/a joven'
  elif (x > 30) and (x <= 60):
    return 'Adulto/a'
  else:
    return 'Adulto/a mayor'

# agregamos una columna nueva clasificando cada fila de acuerdo
# a su edad
df_age['Rango_edad'] = df_age.age.apply(lambda x: rango_edad(x))
df_age.head()
Out[ ]:
age Rango_edad
0 22.0 Adulto/a joven
1 38.0 Adulto/a
2 26.0 Adulto/a joven
3 35.0 Adulto/a
4 35.0 Adulto/a
In [ ]:
# veremos el histograma de las frecuencia de la variable survived
df_age.Rango_edad.hist(color='#01B9FA')
plt.title('Frecuencia para la variable survived', size=16)
plt.ylabel('Frecuencia')
plt.show()

Vemos que en su mayoría las personas abordo eran adultos jóvenes (18-30 años de edad).

Por otro lado, podemos ver de manera directa un gráfico referente al comportamiento de las edades de los tripulantes:

In [ ]:
df_age.sort_values('age', ascending=False)
Out[ ]:
age Rango_edad
630 80.00 Adulto/a mayor
851 74.00 Adulto/a mayor
96 71.00 Adulto/a mayor
493 71.00 Adulto/a mayor
116 70.50 Adulto/a mayor
... ... ...
831 0.83 Niño/a
469 0.75 Niño/a
644 0.75 Niño/a
755 0.67 Niño/a
803 0.42 Niño/a

891 rows × 2 columns

In [ ]:
# iloc[x] accede a la fila x
df_age.sort_values('age', ascending=False).iloc[0]
Out[ ]:
age                     80.0
Rango_edad    Adulto/a mayor
Name: 630, dtype: object
In [ ]:
df_age.sort_values('age', ascending=False).iloc[1]
Out[ ]:
age                     74.0
Rango_edad    Adulto/a mayor
Name: 851, dtype: object
In [ ]:
df_age.sort_values('age', ascending=False).iloc[1][0]
Out[ ]:
74.0
In [ ]:
df_age.sort_values('age', ascending=False).iloc[1][1]
Out[ ]:
'Adulto/a mayor'
In [ ]:
df_age.sort_values('age', ascending=False).iloc[1][2]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-31-1cff3f22427b> in <module>
----> 1 df_age.sort_values('age', ascending=False).iloc[1][2]

/usr/local/lib/python3.7/dist-packages/pandas/core/series.py in __getitem__(self, key)
    937 
    938         if is_integer(key) and self.index._should_fallback_to_positional():
--> 939             return self._values[key]
    940 
    941         elif key_is_scalar:

IndexError: index 2 is out of bounds for axis 0 with size 2
In [ ]:
# Definimos una funcion que nos arroje la edad dada una fila del dataframe,
# donde dicho dataframe estara previamente ordenado de mayor a menor
f = lambda x: df_age.sort_values('age', ascending=False).iloc[x][0]

# Graficaremos la edad de los tripulantes f(0)
plt.plot([i for i in range(len(df_age))], [f(i) for i in range(len(df_age))])
plt.xlabel('Número de personas')
plt.ylabel('Edades')
plt.show()
In [ ]:
241 / 891 * 100
Out[ ]:
27.048260381593714

Notamos que, en efecto, hubo muchas personas consideradas adultos jóvenes, donde podemos ver que, en particular, hubo una gran cantidad de personas cuya edad es muy cercana a los 30. Asimismos, podemos ver que la mayoría de las personas tuvieron una edad entre los 18 y los 30 años; hubo pocas personas que tuvieron una edad menor a los 10 años, al igual que muy pocas personas mayores de 60 años.


¿Cuántas personas tuvieron una edad entre los 29 y 31 años?

In [ ]:
# Escribe el codigo necesario para responder la pregunta anterior
df[(df.age >= 29) & (df.age <= 31)]
Out[ ]:
survived pclass age sibsp parch fare male age_was_missing embarked_from_cherbourg embarked_from_queenstown embarked_from_southampton
5 0 3 29.699118 0 0 8.4583 1 True 0 1 0
17 1 2 29.699118 0 0 13.0000 1 True 0 0 1
18 0 3 31.000000 1 0 18.0000 0 False 0 0 1
19 1 3 29.699118 0 0 7.2250 0 True 1 0 0
26 0 3 29.699118 0 0 7.2250 1 True 1 0 0
... ... ... ... ... ... ... ... ... ... ... ...
863 0 3 29.699118 8 2 69.5500 0 True 0 0 1
867 0 1 31.000000 0 0 50.4958 1 False 0 0 1
868 0 3 29.699118 0 0 9.5000 1 True 0 0 1
878 0 3 29.699118 0 0 7.8958 1 True 0 0 1
888 0 3 29.699118 1 2 23.4500 0 True 0 0 1

241 rows × 11 columns


Finalmente, trataremos de ver si existen relaciones entre las variables de nuestro conjunto de datos, para lo cual analizaremos:

  • Diagramas de dispersión entre las variables de interés. Donde los diagramas de dispersión nos sirven, en particular, justo para visualizar posibles relaciones entre dos variables.
  • Coeficiente de correlación de Pearson, el cual nos indicará la posibilidad de relación entre dos variables de interés. Dicho coeficiente va del -1 al 1, donde 1 nos dice que existe fuertemente la relación entre las variables (esto es, si una de ellas aumenta, la otra también), para valores cercanos a cero nos indicaría que no hay relación y para valores cercanos a -1 que hay una relación inversa (si una aumenta, la otra disminuye y viceversa).

Comenzaremos realizando algunos diagramas de dispersión, donde debemos tener cuidado pues, para dos variables en cuestión éstas deben ser ambas cuantitativas para poder realizar un gráfico de este tipo. De tal manera tendremos sólo un posible diagrama de dispersión:

In [ ]:
df
Out[ ]:
survived pclass age sibsp parch fare male age_was_missing embarked_from_cherbourg embarked_from_queenstown embarked_from_southampton
0 0 3 22.000000 1 0 7.2500 1 False 0 0 1
1 1 1 38.000000 1 0 71.2833 0 False 1 0 0
2 1 3 26.000000 0 0 7.9250 0 False 0 0 1
3 1 1 35.000000 1 0 53.1000 0 False 0 0 1
4 0 3 35.000000 0 0 8.0500 1 False 0 0 1
... ... ... ... ... ... ... ... ... ... ... ...
886 0 2 27.000000 0 0 13.0000 1 False 0 0 1
887 1 1 19.000000 0 0 30.0000 0 False 0 0 1
888 0 3 29.699118 1 2 23.4500 0 True 0 0 1
889 1 1 26.000000 0 0 30.0000 1 False 1 0 0
890 0 3 32.000000 0 0 7.7500 1 False 0 1 0

891 rows × 11 columns

In [ ]:
df.plot.scatter(x='age', y='fare')
plt.title('Age vs Fare')
plt.show()

donde notamos que, al parecer, dichas variables no se encuentran relacionadas. Corroboremos lo anterior mediante el coeficiente de correlación de Pearson:

In [ ]:
# el siguiente codigo nos arrojara una tabla con el coeficiente de 
# correlacion de pearson entre todas las variables de nuestro
# conjunto de datos
df.corr(method='pearson')
Out[ ]:
survived pclass age sibsp parch fare male age_was_missing embarked_from_cherbourg embarked_from_queenstown embarked_from_southampton
survived 1.000000 -0.338481 -6.980852e-02 -0.035322 0.081629 0.257307 -0.543351 -9.219652e-02 0.168240 0.003650 -0.155660
pclass -0.338481 1.000000 -3.313388e-01 0.083081 0.018443 -0.549500 0.131900 1.729329e-01 -0.243292 0.221009 0.081720
age -0.069809 -0.331339 1.000000e+00 -0.232625 -0.179191 0.091566 0.084153 -1.303789e-16 0.032024 -0.013855 -0.027121
sibsp -0.035322 0.083081 -2.326246e-01 1.000000 0.414838 0.159651 -0.114631 1.895757e-02 -0.059528 -0.026354 0.070941
parch 0.081629 0.018443 -1.791909e-01 0.414838 1.000000 0.216225 -0.245489 -1.241038e-01 -0.011069 -0.081228 0.063036
fare 0.257307 -0.549500 9.156609e-02 0.159651 0.216225 1.000000 -0.182333 -1.007071e-01 0.269335 -0.117216 -0.166603
male -0.543351 0.131900 8.415344e-02 -0.114631 -0.245489 -0.182333 1.000000 5.521512e-02 -0.082853 -0.074115 0.125722
age_was_missing -0.092197 0.172933 -1.303789e-16 0.018958 -0.124104 -0.100707 0.055215 1.000000e+00 0.033270 0.337413 -0.238377
embarked_from_cherbourg 0.168240 -0.243292 3.202442e-02 -0.059528 -0.011069 0.269335 -0.082853 3.326967e-02 1.000000 -0.148258 -0.778359
embarked_from_queenstown 0.003650 0.221009 -1.385524e-02 -0.026354 -0.081228 -0.117216 -0.074115 3.374132e-01 -0.148258 1.000000 -0.496624
embarked_from_southampton -0.155660 0.081720 -2.712109e-02 0.070941 0.063036 -0.166603 0.125722 -2.383767e-01 -0.778359 -0.496624 1.000000

Notamos que para age versus fare el coeficiente es de 0.091566, esto es, las variables no están relacionadas.

Notamos también que, para survived versus male el coeficiente es de -0.543351, lo cual nos da indicios de una posible relación inversa.