{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "intro_to_pandas.ipynb", "version": "0.3.2", "views": {}, "default_view": {}, "collapsed_sections": [ "JndnmDMp66FL", "YHIWvc9Ms-Ll", "TJffr5_Jwqvd" ] } }, "cells": [ { "metadata": { "id": "JndnmDMp66FL", "colab_type": "text" }, "source": [ "#### Copyright 2017 Google LLC." ], "cell_type": "markdown" }, { "metadata": { "id": "hMqWDc_m6rUC", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } }, "cellView": "both" }, "source": [ "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "rHLcriKWLRe4", "colab_type": "text" }, "source": [ " # Introducción rápida a Pandas"]}, { "metadata": { "id": "QvJBqX8_Bctk", "colab_type": "text" }, "cell_type": "markdown", "source": [ "**Objetivos de aprendizaje:**\n", " * Obtener una introducción a las estructuras de datos de `DataFrame` y `Series` de la biblioteca de *Pandas*\n", " * Acceder y manipular datos dentro de `DataFrame` y `Series`\n", " * Importar datos CSV a un `DataFrame` de *Pandas*\n", " * Reindexar un `DataFrame` para obtener datos aleatorios" ] }, { "cell_type": "markdown", "metadata": { "id": "TIFJ83ZTBctl", "colab_type": "text" }, "source": [ " [*pandas*](http://pandas.pydata.org/) es una API de análisis de datos en columnas, ideal para manipular y analizar datos de entrada. Además, muchos marcos de trabajo de AA admiten las estructuras de datos *pandas* como entradas.\n", "Si bien una introducción detallada a la API de *pandas* abarcaría muchas páginas, los conceptos principales que presentamos a continuación son simples. Para obtener una referencia más completa, el [sitio de documentación de *pandas*](http://pandas.pydata.org/pandas-docs/stable/index.html) incluye una documentación exhaustiva y numerosos instructivos."]}, { "cell_type": "markdown", "metadata": { "id": "s_JOISVgmn9v", "colab_type": "text" }, "source": [ " ## Conceptos básicos\n", "\n", "La siguiente línea importa la API de *pandas* e imprime la versión de la API:"]}, { "metadata": { "id": "aSRYu62xUi3g", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "import pandas as pd\n", "pd.__version__" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "daQreKXIUslr", "colab_type": "text" }, "source": [ " Las estructuras de datos principales en *pandas* están implementadas en dos clases:\n", "\n", " * **`DataFrame`**, que puedes imaginar como una tabla de datos relacional, con filas y columnas con nombre.\n", " * **`Series`**, que es una columna simple. Una clase `DataFrame` incluye una o más `Series` y un nombre para cada `Series`.\n", "\n", "El marco de datos es una abstracción que se usa normalmente para manipular datos. Hay implementaciones similares en [Spark](https://spark.apache.org/) y [R](https://www.r-project.org/about.html)."]}, { "cell_type": "markdown", "metadata": { "id": "fjnAk1xcU0yc", "colab_type": "text" }, "source": [ " Una manera de crear una `Series` es construir un objeto de `Series`. Por ejemplo:"]}, { "metadata": { "id": "DFZ42Uq7UFDj", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "pd.Series(['San Francisco', 'San Jose', 'Sacramento'])" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "U5ouUp1cU6pC", "colab_type": "text" }, "source": [ " Los objetos de `DataFrame` pueden crearse al enviar un `dict` que asigne nombres de columnas de `string` a sus `Series` correspondientes. Si las `Series` no coinciden con la longitud, los valores que falten se completan con valores [NA/NaN](http://pandas.pydata.org/pandas-docs/stable/missing_data.html) especiales. Ejemplo:"]}, { "metadata": { "id": "avgr6GfiUh8t", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento'])\n", "population = pd.Series([852469, 1015785, 485199])\n", "\n", "pd.DataFrame({ 'City name': city_names, 'Population': population })" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "oa5wfZT7VHJl", "colab_type": "text" }, "source": [ " Pero por lo general, cargas un archivo completo en un `DataFrame`. El siguiente ejemplo carga un archivo con datos de viviendas de California. Ejecuta la siguiente celda para cargar los datos y crear definiciones de funciones:"]}, { "metadata": { "id": "av6RYOraVG1V", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "california_housing_dataframe = pd.read_csv(\"https://storage.googleapis.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n", "california_housing_dataframe.describe()" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "WrkBjfz5kEQu", "colab_type": "text" }, "source": [ " En el ejemplo de arriba, se usó `DataFrame.describe` para mostrar estadísticas interesantes sobre un `DataFrame`. Otra función útil es `DataFrame.head`, que muestra los primeros registros de un `DataFrame`:"]}, { "metadata": { "id": "s3ND3bgOkB5k", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "california_housing_dataframe.head()" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "w9-Es5Y6laGd", "colab_type": "text" }, "source": [ " Otra función util de *pandas* es la generación de gráficos. Por ejemplo, `DataFrame.hist` permite estudiar rápidamente la distribución de los valores en una columna:"]}, { "metadata": { "id": "nqndFVXVlbPN", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "california_housing_dataframe.hist('housing_median_age')" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "XtYZ7114n3b-", "colab_type": "text" }, "source": [ " ## Acceso a los datos\n", "\n", "Puedes acceder a los datos de `DataFrame` mediante las operaciones convencionales de dict/list de Python:"]}, { "metadata": { "id": "_TFm7-looBFF", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities = pd.DataFrame({ 'City name': city_names, 'Population': population })\n", "print type(cities['City name'])\n", "cities['City name']" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "metadata": { "id": "V5L6xacLoxyv", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "print type(cities['City name'][1])\n", "cities['City name'][1]" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "metadata": { "id": "gcYX1tBPugZl", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "print type(cities[0:2])\n", "cities[0:2]" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "65g1ZdGVjXsQ", "colab_type": "text" }, "source": [ " Además, *pandas* proporciona una API muy enriquecida para una [indexación y selección](http://pandas.pydata.org/pandas-docs/stable/indexing.html) avanzadas, que es un tema demasiado amplio como para cubrirlo aquí."]}, { "cell_type": "markdown", "metadata": { "id": "RM1iaD-ka3Y1", "colab_type": "text" }, "source": [ " ## Manipulación de datos\n", "\n", "Puedes aplicar operaciones aritméticas básicas de Python a las `Series`. Por ejemplo:"]}, { "metadata": { "id": "XWmyCFJ5bOv-", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "population / 1000." ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "TQzIVnbnmWGM", "colab_type": "text" }, "source": [ " [NumPy](http://www.numpy.org/) es un kit de herramientas popular para el cálculo científico. Las `Series` de *pandas* pueden usarse como argumentos para la mayoría de las funciones NumPy:"]}, { "metadata": { "id": "ko6pLK6JmkYP", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "import numpy as np\n", "\n", "np.log(population)" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "xmxFuQmurr6d", "colab_type": "text" }, "source": [ " Para obtener información sobre transformaciones más complejas de una sola columna, puedes usar `Series.apply`. Al igual que la función [función map](https://docs.python.org/2/library/functions.html#map) de Python, `Series.apply` acepta como argumento una función [función lambda](https://docs.python.org/2/tutorial/controlflow.html#lambda-expressions), que se aplica a cada valor.\n", "\n", "El siguiente ejemplo crea una nueva `Series` que indica si la `population` es superior a un millón:"]}, { "metadata": { "id": "Fc1DvPAbstjI", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "population.apply(lambda val: val > 1000000)" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "ZeYYLoV9b9fB", "colab_type": "text" }, "source": [ " \n", "Modificar `DataFrames` también es simple. Por ejemplo, el siguiente código agrega dos `Series` a un `DataFrame` existente:"]}, { "metadata": { "id": "0gCEX99Hb8LR", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities['Area square miles'] = pd.Series([46.87, 176.53, 97.92])\n", "cities['Population density'] = cities['Population'] / cities['Area square miles']\n", "cities" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "6qh63m-ayb-c", "colab_type": "text" }, "source": [ " ## Ejercicio n.º 1\n", "\n", "Para modificar la tabla de `cities`, agrega una nueva columna booleana que sea Verdadera si y solo si *ambos* de los siguientes valores son Verdaderos:\n", "\n", " * La ciudad le debe su nombre a un santo.\n", " * La ciudad tiene un área superior a 50 millas cuadradas.\n", "\n", "**Nota:** Las `Series` booleanas se combinan en función de los bits, en lugar de los operadores booleanos tradicionales. Por ejemplo, cuando utilices *logical and*, usa `&` en lugar de `and`.\n", "\n", "**Hint:** \"San\" en español representa \"santo\"."]}, { "metadata": { "id": "zCOn8ftSyddH", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "# Your code here" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "YHIWvc9Ms-Ll", "colab_type": "text" }, "source": [ " ### Solución\n", "\n", "Haz clic a continuación para obtener una solución."]}, { "metadata": { "id": "T5OlrqtdtCIb", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities['Is wide and has saint name'] = (cities['Area square miles'] > 50) & cities['City name'].apply(lambda name: name.startswith('San'))\n", "cities" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "f-xAOJeMiXFB", "colab_type": "text" }, "source": [ " ## Índices\n", "Los objetos `Series` y `DataFrame` también definen una propiedad de `index` que asigna un valor de identificador a cada elemento `Series` o fila `DataFrame`.\n", "\n", "De forma predeterminada, en la construcción, *pandas* asigna valores de índice que reflejan la solicitud de los datos de origen. Una vez creados, los valores de índice son estables, es decir, no cambian cuando cambia el orden de los datos."]}, { "metadata": { "id": "2684gsWNinq9", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "city_names.index" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "metadata": { "id": "F_qPe2TBjfWd", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.index" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "hp2oWY9Slo_h", "colab_type": "text" }, "source": [ " Llama `DataFrame.reindex` para cambiar el orden de las filas de forma manual. Por ejemplo, la siguiente acción tiene el mismo efecto que ordenar los valores por nombre de ciudad:"]}, { "metadata": { "id": "sN0zUzSAj-U1", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.reindex([2, 0, 1])" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "-GQFz8NZuS06", "colab_type": "text" }, "source": [ " La reindexación es una excelente manera de seleccionar un `DataFrame` de forma aleatoria. En el ejemplo que se muestra a continuación, tomamos el índice, que es del tipo matriz, y lo enviamos a la función `random.permutation` de NumPy, que selecciona sus valores de forma aleatoria. Utilizar la `reindexación` con esta matriz aleatoria provoca que las filas de `DataFrame` se seleccionen de forma aleatoria de la misma manera.\n", "¡Prueba ejecutar la siguiente celda varias veces!"]}, { "metadata": { "id": "mF8GC0k8uYhz", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.reindex(np.random.permutation(cities.index))" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "fSso35fQmGKb", "colab_type": "text" }, "source": [ " Para obtener más información, consulta [Documentación de índice](http://pandas.pydata.org/pandas-docs/stable/indexing.html#index-objects)."]}, { "cell_type": "markdown", "metadata": { "id": "8UngIdVhz8C0", "colab_type": "text" }, "source": [ " ## Ejercicio n.º 2\n", "\n", "El método de `reindex` permite los valores de índice que no están en los valores de índice originales de `DataFrame`. Pruébalo y observa lo que sucede si usas esos valores. ¿Por qué supones que los permite?"]}, { "metadata": { "id": "PN55GrDX0jzO", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "# Your code here" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "TJffr5_Jwqvd", "colab_type": "text" }, "source": [ " ### Solución\n", "\n", "Haz clic a continuación para conocer la solución."]}, { "cell_type": "markdown", "metadata": { "id": "8oSvi2QWwuDH", "colab_type": "text" }, "source": [ " Si la matriz de la entrada `reindex` incluye valores que no se encuentran en los valores de índice originales `DataFrame`, `reindex` agregará nuevas filas para esos índices \"faltantes\" y completará todas las columnas correspondientes con los valores `NaN`:"]}, { "metadata": { "id": "yBdkucKCwy4x", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.reindex([0, 4, 5, 2])" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "2l82PhPbwz7g", "colab_type": "text" }, "source": [ " Este comportamiento es util, ya que por lo general los índices son strings extraídos de los datos actuales (consulta la [*pandas* reindex\n", "documentation](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.reindex.html) para obtener un ejemplo en el que los valores de índice son nombres de navegadores).\n", "\n", "En este caso, permitir los índices \"faltantes\" facilita la reindexación mediante una lista externa, dado que no tienes que preocuparte por sanear directamente las entradas."]} ] }