<jupyter_start><jupyter_text>Pandas[_Pandas_](https://pandas.pydata.org/) is a Python package that provides fast, flexible, and expressive data structures designed to make working with "relational" or "labeled" data both easy and intuitive.  We will learn about:* How to create and store a DataFrame.* How to open a file.* Some way to work with a DataFrame.* plots---[_Pandas_](https://pandas.pydata.org/)  es un paquete de Python que proporciona estructuras de datos rápidas y flexibles diseñadas para que el trabajo con datos "relacionales" o "etiquetados" sea fácil e intuitivo.Vamos a aprender sobre:* Cómo crear y almacenar un DataFrame.* Cómo abrir un archivo.* Alguna formas para trabajar con un DataFrame.* Graficar  Import Pandas / Importar Pandas<jupyter_code>import pandas as pd<jupyter_output><empty_output><jupyter_text>We also import other packages that we're going use in this notebook.---También importamos otros paquete que usaremos en este notebook.<jupyter_code>from pathlib import Path
import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>How to create data? / Como crear datos? DataFrameA DataFrame is a two-dimensional data structure, that is, the data is tabular aligned in rows and columns. One way to create a DataFrame is through a dictionary. ---Un DataFrame es una estructura de datos bidimensional, es decir, los datos tienen un formato tabular en filas y columnas.Una forma de crear un DataFrame es a través de un diccionario.<jupyter_code>dictionary_data = {
    "Rocío": ["Peruana", "USA", 40],
    "Clara": ["Venezolana", "UK", 25],
    "Agustina": ["Argentina", "Canada", 36]
}    

data_frame = pd.DataFrame(dictionary_data)

print(data_frame)<jupyter_output>Rocío       Clara   Agustina
0  Peruana  Venezolana  Argentina
1      USA          UK     Canada
2       40          25         36<jupyter_text>As we can see, the indices go in ascending order starting from 0, but this can be changed using `index` when building the dataframe.---Como vemos, los indices van en orden ascendente a partir de 0, pero esto se puede cambiar usando `index` al construir el dataframe.<jupyter_code>data_frame = pd.DataFrame(
    dictionary_data, 
    index=["Paìs de origen", "Lugar de residencia", "Edad"]
)

data_frame<jupyter_output><empty_output><jupyter_text>To save the DataFrame with `csv`:---Para guardar un DadaFrame como `csv`:<jupyter_code>data_frame.to_csv("miembros.csv")<jupyter_output><empty_output><jupyter_text>See the Pandas documentation to find other methods to save a DataFrame.---Investigue la documentación de Pandas para encontrar otros métodos para guardar un DataFrame.  SeriesIf the DataFrame is a table, a `Series` is a "column" and can be created from a list.----Si el DataFrame es una tabla, una `Serie` es una "columna" y se puede crear a partir de una lista.<jupyter_code>col = pd.Series([0, 40, 400])

col<jupyter_output><empty_output><jupyter_text>A `Series` is essentially a simple column of a DataFrame, so indexes can be assigned, however the Series does not have a column name but it has a general name` Name`---Una `Serie` es en esencia una columna simple de un DataFrame, así que se pueden asignar índices, sin embargo la Serie no tiene un nombre de columna sino un nombre general `Name`<jupyter_code>col = pd.Series(
    [0, 40, 400], 
    index=['2015 ', '2016', '2021'], 
    name='Numero de miembros'
)

col<jupyter_output><empty_output><jupyter_text>Open a File / Abrir un archivoWe are going to work with data that we have in a _.csv_ file "comma-separated values"---Vamos ahora a trabajar con datos que tenemos en un archivo _.csv_ "comma-separated values"<jupyter_code># Path to the file / Ruta al archivo
data_folder = Path(".") / "data"

# Open the file / Abrimos el archivo
data = pd.read_csv(data_folder / "RPC_4_lithologies_Messy.csv")<jupyter_output><empty_output><jupyter_text>To have a look at the first 5 rows of DataFrame, we can use the `head` method.---Para echar un vistazo a las primeras 5 filas de DataFrame, podemos usar el método `head`.<jupyter_code>data.head()<jupyter_output><empty_output><jupyter_text>**Question / Pregunta**1) What do you think the method `.tail()` does?---1) ¿Qué creen que hace el metodo `.tail()`?<jupyter_code>data.tail()<jupyter_output><empty_output><jupyter_text>2) How do you think you can see the first 20 rows of my DataFrame?----2) ¿Cómo creen que puedo ver las 20 primeras filas de mi DataFrame?<jupyter_code>data.head(20)<jupyter_output><empty_output><jupyter_text>We use the `shape` attribute to know how big the DataFrame is.---Usamos el atributo `shape` para saber que tan grande es el DataFrame:<jupyter_code>data.shape<jupyter_output><empty_output><jupyter_text>`DataFrame.describe()` gets the summary statistics of only the columns that have numerical data.All other columns are ignored, unless you use the argument `include="all"`.---`DataFrame.describe()` muestra una estadística de las columnas que tienen datos numéricos.Todas las demás columnas se ignoran, a menos que use el argumento `include="all"`.<jupyter_code>data.describe()<jupyter_output><empty_output><jupyter_text>DataFrame columnsIf we want to access the _Description_ property of the DataFrame, this notation can be used:---Si quiero acceder a la propiedad _Description_ del DataFrame se puede usar esta notación:<jupyter_code>data["Description"]
data["Vs [km/s]"]<jupyter_output><empty_output><jupyter_text>Selecting values / Seleccionando valoresTo access a value at the position `[i, j]` of a DataFrame, we have two options, using the index or the label.Remember that a DataFrame provides an index as a way to identify the rows of the table; a row, then, has a position inside the table as well as a label, which uniquely identifies its entry in the DataFrame.---Para acceder a un valor en la posición `[i, j]` de un DataFrame, tenemos dos opciones, usando el *indice o la etiqueta*.Recuerde que un DataFrame proporciona un índice como una forma de identificar las filas de la tabla; una fila, entonces, tiene una posición dentro de la tabla, así como una etiqueta, que identifica de forma única su entrada en el DataFrame.<jupyter_code>data.head()<jupyter_output><empty_output><jupyter_text>Using the position / Usando la posiciónUse `DataFrame.iloc[..., ...]` to select values by their position.`[i, j]` The first one is the row position and the second one is the column position.---Utilice `DataFrame.iloc [..., ...]` para seleccionar valores por su posición.`[i, j]` El primero es la posición de la fila y el segundo es la posición de la columna.<jupyter_code>data.iloc[0, 0]
data.iloc[:, -1]<jupyter_output><empty_output><jupyter_text>We can use what we saw in the first notebook about indexes.---Podemos usar lo que vimos en el primer notebook sobre los índices. **Question / Pregunta**3) How can I see the densities that are between rows 100 and 150?---3) ¿Cómo puedo ver las densidades que están entre las filas 100 y 150?<jupyter_code>data.iloc[100:151, -1]<jupyter_output><empty_output><jupyter_text>Using the label / Usando la etiquetaUse `DataFrame.loc[..., ...]` to select values by their label.`[index, label]` The first one is the row index and the second one is the column label.---Use `DataFrame.loc[..., ...]` para seleccionar valores por su etiqueta.`[índice, etiqueta]` El primero es el índice de la fila y el segundo es la etiqueta de la columna.<jupyter_code>data.loc[3, "RPC"]
data.loc[3:10, "RPC"]
data.loc[3:10, ["RPC", "Source", "Rho [kg/m³]"]]<jupyter_output><empty_output><jupyter_text>Conditional selection / Selección condicionalWe use the conditional syntax to find the data that satisfies a given condition.---Usamos la sintaxis condicional para encontrar los datos que satisfacen una dada condición. For example, we can check if there are densities greater than 2000 $kg/m^3$:---Por ejemplo podemos chequear si hay densidades mayores a 2000 $kg/m^3$:<jupyter_code>data["Rho [kg/m³]"] > 2000<jupyter_output><empty_output><jupyter_text>**Question / Pregunta**4) We want all the data where the lithology is sandstone. What would the condition be like?---4) Queremos todos los datos donde la litología es arenisca. ¿Cómo sería la condición?<jupyter_code>condicion = data["Lithology"] == "sandstone"

data[condicion]<jupyter_output><empty_output><jupyter_text>Cleaning our data using Pandas / Limpiando nuestra data usando PandasThe DataFrame loaded before is a smaller dataset of lab data (Vp, Vs, Rho) for 800 samples from the _Rock Property Catalog_, which has some problems like:* repeated rows* missing data* different nomenclature* redundant information, etc.So, before doing any plot or analysis of the DataFrame data, we need to examine the data and fix the this.---El DataFrame cargado antes es un conjunto mas pequeño de datos de laboratorio (Vp, Vs, Rho) para 800 muestras de un _Catalogo de Propiedades de las Rocas_. Éste tiene algunos problemas como:* filas repetidas* datos faltantes* nomenclatura diferente* información redundante, etc. Entonces, antes de realizar cualquier grafica o analisis de los datos del DataFrame, necesitamos examinar los datos y arreglar los problemas mencionados. Reload the DataFrame again to avoid errors---Recargamos otra vez el DataFrame para no tener errores<jupyter_code>data = pd.read_csv(data_folder / "RPC_4_lithologies_Messy.csv")

data.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 808 entries, 0 to 807
Data columns (total 8 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   Index        808 non-null    int64  
 1   RPC          808 non-null    int64  
 2   Source       808 non-null    object 
 3   Description  808 non-null    object 
 4   Lithology    808 non-null    object 
 5   Vs [km/s]    808 non-null    float64
 6   VpVs ratio   808 non-null    float64
 7   Rho [kg/m³]  760 non-null    float64
dtypes: float64(3), int64(2), object(3)
memory usage: 50.6+ KB<jupyter_text>Finding _null_ values in our DataFrame / Encontrando valores _null_ en nuestro DataFrameWe use the `isnull` method, which returns a Boolean object of the same size as the DataFrame that indicates if the values are _NaN_.`any` returns _True_ if any element of the iterable is true.If the iterable is empty, return _False_. ---Usamos el método `isnull` que devuelve un objeto booleano del mismo tamaño del DataFrame que indica si los valores son _NaN_.`any` devuelve _True_ si algún elemento del iterable es verdadero.Si el iterable está vacío, devuelve _False_.<jupyter_code>data.isnull()
data.isnull().values.any()<jupyter_output><empty_output><jupyter_text>Now, we make a fast plot to check this. ---Ahora, hacemos una gráfica rápida para chequear lo mencionado.<jupyter_code>names = ["RPC", "Vs [km/s]", "VpVs ratio", "Rho [kg/m³]"]

fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, figsize=(15, 5))

data[names[0]].plot(ax=axes[0, 0])
axes[0,0].set_title(names[0])

data[names[1]].plot(ax=axes[0, 1])
axes[0,1].set_title(names[1])

data[names[2]].plot(ax=axes[1, 0])
axes[1,0].set_title(names[2])

data[names[3]].plot(ax=axes[1, 1])
axes[1,1].set_title(names[1])
plt.show()<jupyter_output><empty_output><jupyter_text>Another way to plot this is using a for loop:---Otra forma de graficár esto es usando un *for loop*:<jupyter_code>names = ["RPC", "Vs [km/s]", "VpVs ratio", "Rho [kg/m³]"]

fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, figsize=(15, 5))

axes = axes.flatten()

for name, i in zip(names, range(axes.size)):
    data[name].plot(ax=axes[i])
    axes[i].set_title(name)
plt.show()<jupyter_output><empty_output><jupyter_text>The density column has NaN values, so we will use the `dropna` method to remove the row which only has NaN values.---La columna de densidad tiene valores de NaN, por lo que usaremos el método `dropna` para eliminar la fila que solo tiene valores de NaN.<jupyter_code>data = data.dropna()

data.shape<jupyter_output><empty_output><jupyter_text>We've removed 48 rows, so the DataFrame has 760 rows.---Hemos eliminado 48 filas, por lo que el DataFrame tiene 760 filas.  Remove duplicates rowsNow, we will find and remove the duplicates rows using `duplicated()`. ---Ahora vamos a encontrar y eliminar la filas repetidas usando `duplicated()`. Select duplicate rows except first occurrence based on all columns.---Selecciona filas duplicadas excepto la primera aparición basada en todas las columnas.<jupyter_code>data[data.duplicated()]<jupyter_output><empty_output><jupyter_text>Remove the duplicated rows:---Eliminamos las filas duplicadas:<jupyter_code>data = data.drop_duplicates()

data.shape<jupyter_output><empty_output><jupyter_text>We've removed 8 rows, so the DataFrame has 752 rows. ---Hemos eliminado 8 filas, por lo que el DataFrame tiene 752 filas. Check that it does not have repeating rows:---Comprueba que no tenga filas repetidas:<jupyter_code>data[data.duplicated()].any()<jupyter_output><empty_output><jupyter_text>Fixing values / Corriginedo valoresWe need to fix some lithology names.We will change _SANDST._ for _sandstone_.To do that, we use the `replace` method.---Necesitamos arreglar algunos nombres de litología.Cambiaremos _SANDST._ por _sandstone_.Para ello usamos el metodo `replace`.<jupyter_code>data.head()
data["Lithology"] = data["Lithology"].replace("SANDST.", "sandstone")

data.head()<jupyter_output><empty_output><jupyter_text>Adding a column / Agregando una columnaThe DataFrame doesn't have the value of _Vp_.So, we are going to calculate and add it to the DataFrame as a new column.---El DataFrame no tiene el valor de _Vp_.Entonces, nosotros vamos a calcularlo y agregarlo en el DataFrame como una nueva columna.$$ V_p V_s ratio = \frac{V_p}{V_s} $$<jupyter_code>data["Vp [km/s]"] = data["VpVs ratio"] / data["Vs [km/s]"]

data.head()<jupyter_output><empty_output><jupyter_text>Removing columns / Eliminando columnasWe are going to remove from the DataFrame the columns that are not relevant: `Index,  Description y VpVs ratio`  For that, use the `drop` method.---Vamos a eliminar del DataFrame las columnas que no sean relevantes: `Index, Description y VpVs ratio`.Para ello uso el metodo `drop`.<jupyter_code>data.drop(["Index", "Description", "VpVs ratio"], axis=1, inplace=True)  
# axis=1 is because we want to drop columns, see the documentation for more details 
# inplace=True means that we don't generate a copy of the dataFrame, we do it in the same DataFrame, see the documentation for more details.

data<jupyter_output><empty_output><jupyter_text>Plotting our DataFrame / Graficando nuestro DataFrameWe are going to plot _Vp & Vs_ and the _density_ according to the different types of lithology of our DataFrame.Let's see how many types of lithology there are. `value_counts` returns a Series containing counts of unique rows in the DataFrame.---Vamos a graficar _Vp & Vs_ y la _densidad_ según los diferentes tipos de litología de nuestro DataFrame.Veamos cuántos tipos de litología hay.`value_counts()` devuelve una serie que contiene recuentos de cuantas veces aparace un valor en el DataFrame.<jupyter_code>data["Lithology"].value_counts()<jupyter_output><empty_output><jupyter_text>Let's group our data according to  the 4 types of lithology using `groupby()`.---Agrupemos nuestros datos según los 4 tipos de litología usando `groupby()`.<jupyter_code>litologias = data.groupby('Lithology')

print(litologias)
for name, group in litologias:
    print(name, "\n", group)<jupyter_output>dolomite 
         RPC Source Lithology  Vs [km/s]  Rho [kg/m³]  Vp [km/s]
604  102242    RPC  dolomite     2.5558       2318.2   0.652946
605  102096    RPC  dolomite     3.4449       2705.9   0.481030
606  102196    RPC  dolomite     2.8412       2435.4   0.601506
607  102071    RPC  dolomite     3.0348       2573.3   0.541452
608  102089    RPC  dolomite     3.0709       2560.0   0.587971
..      ...    ...       ...        ...          ...        ...
803  102178    RPC  dolomite     2.7592       2414.0   0.607640
804  102225    RPC  dolomite     3.1168       2475.7   0.560639
805  102088    RPC  dolomite     2.9987       2521.1   0.605662
806  102213    RPC  dolomite     2.9331       2470.8   0.621629
807  102086    RPC  dolomite     2.8970       2549.2   0.637211

[200 rows x 6 columns]
limestone 
         RPC Source  Lithology  Vs [km/s]  Rho [kg/m³]  Vp [km/s]
404  100145    RPC  limestone     1.8190    2442.3054   0.999175
405  100146    RPC  limestone     1.8190    2462.3277  [...]<jupyter_text>**Plot _Vs & Vp_ / Grafico _Vs & Vp_**<jupyter_code>fig = plt.figure(figsize=(10,5))

for name, group in litologias:
    plt.plot(
        group["Vs [km/s]"], 
        group["Vp [km/s]"], 
        marker='o', 
        linestyle='',
        ms=6, 
        label=name, 
        alpha=0.5       # transparency / trasparencia
    )

#ax.legend(loc=1)
plt.legend()
plt.xlabel('$V_\mathrm{P}\ [km/s]$', size=12)
plt.ylabel('$V_\mathrm{S}\ [km/s]$', size=12)
plt.show()<jupyter_output><empty_output><jupyter_text>**Plot the _density_ | Grafico la _densidad_**<jupyter_code>fig = plt.figure(figsize=(10,5))

for name, group in litologias:
    plt.hist(
        group["Rho [kg/m³]"], 
        histtype='bar', 
        stacked=True, 
        rwidth=0.8, 
        alpha=0.8, 
        label=name
    )
    
plt.legend()
plt.xlabel('$\\rho\ [g/m^3]$', size=12)
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09962568384681832, 'fraction_numerical': 0.0322487762741146, 'mean_word_length': 4.336816226183159, 'pattern_counts': {'":': 3, '<': 139, '<?xml version=': 0, '>': 140, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '35', 'dir': 'jupyter-structured-clean-dedup', 'id': '234625', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 6258, 'n_tokens_neox': 5828, 'n_words': 2027, 'path': 'coco_5_pandas.ipynb', 'repo_name': 'CodeToCommunicate/CoCoLessons', 'size': '17365'}