Uso de la biblioteca Plotly para la visualización interactiva de datos en Python

    En mi artículo anterior, expliqué cómo se puede usar la biblioteca Pandas para trazar gráficos de series de tiempo y básicos. Si bien las bibliotecas Pandas , Matplotlib y Seaborn son excelentes bibliotecas de trazado de datos, solo pueden trazar gráficos estáticos. Los gráficos estáticos son como imágenes simples no interactivas. En la mayoría de los casos, las gráficas estáticas son suficientes para transmitir la información. Sin embargo, en algunos casos, es posible que desee agregar interactividad de usuario a sus gráficos.

    En este artículo, veremos cómo se puede usar la biblioteca Plotly de Python para trazar gráficos interactivos. Trazaremos los datos geográficos utilizando plotly y explicaremos cómo un usuario puede interactuar con tales parcelas.

    Instalación de bibliotecas necesarias

    Para instalar la biblioteca Plotly usando la utilidad «pip», debe ejecutar el siguiente comando:

    $ pip install plotly
    

    Además de Plotly, también usaremos Cufflinks , que funciona como un conector entre la biblioteca de Pandas y Plotly, y nos ayuda a trazar gráficos interactivos directamente usando un marco de datos de Pandas.

    Para instalar Gemelos usando pip, ejecute el siguiente script:

    $ pip install cufflinks
    

    Importación de bibliotecas necesarias

    Plotly es básicamente una biblioteca en línea que aloja sus visualizaciones de datos; sin embargo, también proporciona un paquete de datos sin conexión que se puede utilizar para dibujar gráficos interactivos sin conexión.

    Antes de que podamos ejecutar Plotly en el cuaderno Jupyter, que estoy usando para ejecutar mis scripts, necesito importar las bibliotecas Plotly y Cufflinks junto con Numpy y Pandas como de costumbre.

    El siguiente script importa las bibliotecas Numpy y Pandas:

    import pandas as pd
    import numpy as np
    %matplotlib inline
    

    A continuación, necesitamos importar las versiones fuera de línea de los módulos de Plotly que usaremos en este artículo. El siguiente script hace eso:

    from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
    

    Antes de que podamos ejecutar nuestros scripts, necesitamos conectar JavaScript a nuestro cuaderno. Dado que los gráficos de Plotly son interactivos, utilizan JavaScript entre bastidores. Los scripts que vamos a ejecutar se ejecutarán en el cuaderno de Jupyter. Para conectar el cuaderno Jupyter con JavaScript, necesitamos ejecutar el siguiente script:

    init_notebook_mode(connected=True)
    

    Finalmente, necesitamos importar la biblioteca Cufflink y asegurarnos de que la usaremos sin conexión. Para hacerlo, ejecute el siguiente script:

    import cufflinks as cf
    cf.go_offline()
    

    Ahora tenemos todo lo que necesitamos para dibujar gráficos Plotly interactivos dentro de nuestros cuadernos de Jupyter.

    Plotly para gráficos básicos

    En esta sección, utilizaremos la biblioteca Plotly para dibujar gráficos interactivos básicos. En la siguiente sección, veremos cómo se puede utilizar Plotly para trazar datos geográficos.

    El conjunto de datos

    El conjunto de datos que vamos a utilizar para esta sección es el conjunto de datos «Consejos» que se descarga de forma predeterminada con la biblioteca de Seaborn. El conjunto de datos contiene información sobre la cantidad gastada por un grupo de personas en el almuerzo y la cena. El conjunto de datos contiene sexo, precio, propinas, edad, tamaño, día, hora y si las personas que almorzaron o cenaron eran fumadores o no.

    La siguiente secuencia de comandos importa el conjunto de datos y muestra las primeras cinco filas del conjunto de datos:

    import seaborn as sns
    
    dataset = sns.load_dataset('tips')
    
    dataset.head()
    

    La salida del script se ve así:

    Desde la salida, se puede ver que nuestro conjunto de datos contiene tres columnas numéricas: total_bill, tip, y sizey cuatro columnas categóricos: sex, smoker, day, y time.

    Antes de usar Plotly para dibujar gráficos interactivos, recordemos cómo usamos Pandas para trazar gráficos estáticos. Llamemos al plot()método en nuestro marco de datos para ver cómo los Pandas trazan gráficos estáticos. Trazaremos los valores de las columnas ‘total_bill’, ‘tip’ y ‘sex’. Mira el siguiente guión:

    Te puede interesar:Biblioteca Seaborn para la visualización de datos en Python: Parte 1
    dataset2 = dataset[["total_bill", "tip", "size"]]
    dataset2.plot()
    

    Puede ver que para trazar un gráfico, simplemente llamamos al plotmétodo en nuestro marco de datos.

    Salida:

    Desde el resultado, puede ver el gráfico de líneas estáticas para la columna ‘total_bill’ y ‘tips’.

    Ahora veamos cómo podemos dibujar gráficos interactivos usando Plotly. En la sección donde importamos las bibliotecas, importamos la plot()función del plotly.offlinemódulo. Para trazar gráficos interactivos usando el marco de datos de Pandas, simplemente necesitamos llamar al iplot()método en lugar del plotmétodo. Eche un vistazo al siguiente ejemplo:

    dataset2.iplot()
    

    Una vez que ejecute el script anterior, debería ver un diagrama de línea interactivo para las columnas total_bill, tipy sexcomo se muestra a continuación:

     

    Si pasa el cursor sobre el gráfico, debería ver que los valores cambian. Puede acercar y alejar el gráfico mediante las opciones disponibles en la esquina superior derecha del gráfico. También puede agregar y quitar columnas del gráfico. Finalmente, también puede guardar el gráfico como una imagen estática.

    En el resto de la sección, trazaremos algunas de las gráficas interactivas más comúnmente utilizadas usando Plotly.

    El diagrama de barras

    Para trazar la gráfica de barras interactivas usando Plotly, puede usar la iplot()función. Debe pasar «barra» como el valor del kindparámetro de la iplot()función. Además, debe pasar la lista de columnas categóricas para las que desea trazar sus gráficos al xatributo. Finalmente, la columna numérica se pasa como valor al yatributo. La siguiente secuencia de comandos traza un diagrama de barras para las columnas timey sexen el eje xy en el eje y total_bill.

    dataset.iplot(kind='bar', x=['time', 'sex'],y='total_bill')
    

    Salida:

     

    Puede ver en la salida que se han trazado cuatro barras para la factura total. Las barras muestran todas las combinaciones posibles de valores en las columnas sexy time.

    Además de especificar columnas para los gráficos de barras, puede simplemente llamar a una función agregada en el marco de datos de Pandas y luego llamar a la iplot()función y pasar «barra» como el valor del kindatributo. Esto trazará la barra para cada columna numérica de acuerdo con la función agregada. Por ejemplo, si desea trazar el diagrama de barras que contiene los valores medios para total_bill, tipy sizela columna, puede utilizar el siguiente script:

    dataset.mean().iplot(kind='bar')
    

    Salida:

     

    En la salida, se puede ver los diagramas de barras con valores medios para total_bill, tipy sizela columna.

    Además de los diagramas de barras verticales, también puede trazar diagramas de barras horizontales. Todo lo que tienes que hacer es pasar «barh» como atributo al kindparámetro, como se muestra en el siguiente script:

    dataset.mean().iplot(kind='barh')
    

    Salida:

    Te puede interesar:Introducción al estilo de codificación Python

     

    En la salida, se puede ver los diagramas de barras horizontales para los valores medios de total_bill, tipy sizecolumnas.

    El diagrama de dispersión

    Para trazar un diagrama de dispersión interactivo, debe pasar «dispersión» como el valor del kindparámetro de la iplot()función. Además, debe pasar los nombres de las columnas para los ejes xey. El siguiente script traza un diagrama de dispersión para la total_billcolumna en el eje xy la columna en el eje y tip.

    dataset.iplot(kind='scatter', x='total_bill', y='tip', mode="markers")
    

    Salida:

     

    Pase el mouse sobre el gráfico interactivo para ver los valores cambiantes de las columnas total_billy tip.

    El diagrama de caja

    En uno de mis artículos anteriores expliqué qué es un diagrama de caja y cómo podemos dibujarlo usando la biblioteca de Seaborn. El diagrama de caja traza la información de cuartiles para las columnas numéricas. La distancia entre el bigote inferior y la parte inferior del cuadro muestra el primer cuartil. La distancia entre la parte inferior del cuadro y la mitad del cuadro muestra el segundo cuartil. De manera similar, la distancia desde el centro de la caja hasta el extremo superior de la caja cuantifica el tercer cuartil, mientras que la distancia desde la parte superior de la caja hasta el bigote superior muestra el último cuartil.

    Con Plotly, puede dibujar diagramas de caja interactivos. Todo lo que tiene que hacer es pasar el boxvalor as al kindparámetro de la iplot()función como se muestra a continuación:

    dataset2.iplot(kind='box')
    

    En la salida, verá diagramas de caja para todas las columnas numéricas en los datos total_bill, es decir , tipy size.
    Salida:

     

    La trama Hist

    La biblioteca Plotly también se puede usar para trazar gráficos de histograma interactivos para cualquier columna del conjunto de datos. Para hacerlo, debe pasar «hist» como valor al kindparámetro de la iplot()función. También puede especificar el número de contenedores utilizando el binsatributo. El siguiente script traza el histograma de la total_billcolumna:

    dataset['total_bill'].iplot(kind='hist',bins=25)
    

    Salida:

     

    La gráfica de matriz de dispersión

    El diagrama de matriz de dispersión es básicamente un conjunto de todos los diagramas de dispersión para columnas numéricas en su conjunto de datos.

    dataset2.scatter_matrix()
    

     

    La trama de propagación

    El diagrama de distribución muestra la distribución entre dos o más columnas numéricas en cualquier punto en particular. Por ejemplo, para ver la distribución entre total_bily tip, puede utilizar la función de distribución de la siguiente manera:

    dataset[['total_bill','tip']].iplot(kind='spread')
    

    Salida:

     

    Te puede interesar:Tutorial de Python para principiantes absolutos

    En la salida, puede ver que a medida que total_billaumenta, el diferencial entre total_billy tiptambién aumenta.

    Gráficos 3D

    Por último, además de los gráficos 2D, también puede crear gráficos interactivos 3D utilizando la biblioteca Plotly. Por ejemplo, para ver gráfica 3D para total_bill, tipy sizecolumnas, ejecute el siguiente script.

    dataset2 = dataset[["total_bill", "tip", "size"]]
    data = dataset2.iplot(kind='surface', colorscale="rdylbu")
    

    En la salida puede ver el gráfico 3D, puede moverlo, darle la vuelta y acercar y alejar el gráfico.

    Salida:

     

    En esta sección, vimos algunos de los gráficos interactivos más utilizados que se ofrecen en Plotly. En la siguiente sección, veremos cómo se puede usar la biblioteca Plotly para trazar datos geográficos.

    Parcela para parcelas geográficas

    Para dibujar gráficos geográficos con Plotly, usaremos Choropleth Maps. Los mapas de coropletas son tipos especiales de diagramas de trazado que se utilizan para trazar datos geográficos. La documentación detallada sobre cómo usar los mapas de coropletas está disponible aquí .

    En esta sección veremos, con la ayuda de ejemplos, cómo dibujar mapas geográficos de Estados Unidos y del mundo entero. Pero antes de escribir el código para trazar las parcelas geográficas, primero importemos las bibliotecas deseadas.

    import plotly.plotly as py
    import plotly.graph_objs as go
    from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
    
    init_notebook_mode(connected=True)
    import pandas as pd
    

    Mapas geográficos de Estados Unidos

    Hay cuatro pasos para dibujar mapas geográficos utilizando Plotly.

    Paso 1: crear un diccionario de datos

    El primer paso es crear un diccionario de datos que realmente contenga los datos que desea mostrar en el mapa. Para hacer esto, ejecute el siguiente script, que luego explicaré línea por línea.

    map_data = dict(type="choropleth",
                locations=['MI', 'CO', 'FL', 'IN'],
                locationmode="USA-states",
                colorscale="Portland",
                text=['Michigan', 'Colorado', 'Florida', 'Indiana'],
                z=[1.0,2.0,3.0,4.0],
                colorbar=dict(title="USA States")
               )
    

    Debe especificar valores para varias claves en el diccionario de datos. Son los siguientes:

    • type: Dado que estamos usando mapas de coropletas, el tipo siempre será choropleth.
    • locations: Aquí tenemos que pasar las abreviaturas de los estados que queremos mostrar en nuestro mapa. Se mostrarán cuatro estados en nuestro mapa: ‘Michigan (MI)’, ‘Colorado (CO)’, ‘Florida (FL),’ Indiana (IN) ‘
    • locationmodeserá USA-stateya que solo mostramos el mapa de Estados Unidos.
    • colorscale: Esta tecla se utiliza para especificar el color del gráfico. Consulte la documentación para obtener más opciones de color.
    • text: Contiene una lista de cadenas que se mostrarán cuando el mouse pase sobre la ubicación del estado.
    • La zclave contiene una lista de valores numéricos que se mostrarán cuando el mouse pase sobre la ubicación del estado.
    • colorbares un diccionario. Para la titleclave, puede especificar el texto que se mostrará en la barra de colores.
    Paso 2: crea un diseño

    Una vez que haya creado un diccionario de datos, el siguiente paso es crear el diccionario de diseño. El diccionario de diseño para EE. UU. Es simple, como se muestra en el siguiente script.

    map_layout = dict(geo = {'scope':'usa'})
    

    El diccionario toma otro diccionario llamado geo. Pasamos el valor «usa» para la scopeclave ya que nuestro mapa solo se limita a EE. UU.

    Paso 3: crear objeto gráfico

    Si miras la sección donde importamos las bibliotecas, importamos la plotly.graph_objsclase. El tercer paso es crear un objeto de este gráfico. Para hacerlo, necesitamos llamar a la Figurefunción desde el objeto. El objeto toma dos parámetros: datay layout. Pasaremos nuestro diccionario de datos al primer parámetro y el diccionario de diseño al segundo parámetro, como se muestra a continuación:

    map_actual = go.Figure(data=[map_data], layout=map_layout)
    
    Paso 4: Llame al método iplot ()

    El paso final es llamar a la iplot()función y pasarle el objeto gráfico que creamos en el tercer paso como se muestra a continuación:

    iplot(map_actual)
    

    En el resultado, verá el gráfico geográfico de cuatro estados de EE. UU. Puede ver que el resto del estado no ha sido coloreado ya que no especificamos ninguna información sobre esos estados. Si pasa el mouse sobre los estados coloreados, verá los valores correspondientes de las teclas texty zque especificamos cuando creamos nuestro diccionario de datos.

     

    Mapas geográficos de Estados Unidos con CSV

    Ahora tenemos una idea básica de cómo podemos crear parcelas geográficas usando mapas de Plotly y coropleth. Creemos ahora un mapa más complejo. Veremos cómo podemos usar los datos de un archivo CSV para crear una gráfica geográfica. Crearemos un mapa geográfico que mostrará el PIB per cápita de todos los estados de los Estados Unidos.

    Te puede interesar:Algoritmo de bosque aleatorio con Python y Scikit-Learn
    El conjunto de datos

    El conjunto de datos para esta sección se puede descargar de este enlace de Kaggle , que viene en formato CSV. He cambiado el nombre del archivo por motivos de legibilidad.

    El siguiente script importa el conjunto de datos e imprime sus primeras cinco filas en la consola.

    df = pd.read_csv(r'E:/state_gdp.csv')
    df.head()
    

    La salida se ve así:

    El conjunto de datos contiene los nombres de los estados de EE Area. UU. En la columna. El conjunto de datos también contiene el PIB per cápita para los cinco años de 2013 a 2017. Trazaremos los datos para el año 2017.

    Un problema con el conjunto de datos es que contiene los nombres completos de los estados, mientras que los mapas de coropletas aceptan la abreviatura de los nombres de los estados. Lo primero que debemos hacer es agregar una columna a nuestro conjunto de datos que contenga las abreviaturas de los nombres de los estados.

    Una forma de hacer esto es crear un diccionario para los nombres de los estados y sus abreviaturas y luego crear una columna que contenga valores abreviados de ese diccionario. La siguiente secuencia de comandos crea un diccionario donde las claves son los nombres de estado completos y los valores son las abreviaturas de estado correspondientes:

    us_state_abbrev = {
        'Alabama': 'AL',
        'Alaska': 'AK',
        'Arizona': 'AZ',
        'Arkansas': 'AR',
        'California': 'CA',
        'Colorado': 'CO',
        'Connecticut': 'CT',
        'Delaware': 'DE',
        'Florida': 'FL',
        'Georgia': 'GA',
        'Hawaii': 'HI',
        'Idaho': 'ID',
        'Illinois': 'IL',
        'Indiana': 'IN',
        'Iowa': 'IA',
        'Kansas': 'KS',
        'Kentucky': 'KY',
        'Louisiana': 'LA',
        'Maine': 'ME',
        'Maryland': 'MD',
        'Massachusetts': 'MA',
        'Michigan': 'MI',
        'Minnesota': 'MN',
        'Mississippi': 'MS',
        'Missouri': 'MO',
        'Montana': 'MT',
        'Nebraska': 'NE',
        'Nevada': 'NV',
        'New Hampshire': 'NH',
        'New Jersey': 'NJ',
        'New Mexico': 'NM',
        'New York': 'NY',
        'North Carolina': 'NC',
        'North Dakota': 'ND',
        'Ohio': 'OH',
        'Oklahoma': 'OK',
        'Oregon': 'OR',
        'Pennsylvania': 'PA',
        'Rhode Island': 'RI',
        'South Carolina': 'SC',
        'South Dakota': 'SD',
        'Tennessee': 'TN',
        'Texas': 'TX',
        'Utah': 'UT',
        'Vermont': 'VT',
        'Virginia': 'VA',
        'Washington': 'WA',
        'West Virginia': 'WV',
        'Wisconsin': 'WI',
        'Wyoming': 'WY',
    }
    

    El siguiente paso es agregar una columna en el conjunto de datos que contiene abreviaturas. Podemos hacerlo asignando los valores de la Areacolumna a las claves del us_state_abbrevdiccionario. Los valores correspondientes se pueden agregar a la columna de abreviaturas recién creada como se muestra a continuación:

    df['abbrev'] = df['Area'].map(us_state_abbrev)
    

    Ahora, si vuelve a imprimir la parte superior del marco de datos utilizando la head()función, verá la abbrevcolumna recién creada , como se muestra a continuación:

    df.head()
    

    La salida se ve así:

    Puede ver las abreviaturas de los estados en la abbrevcolumna. Es importante mencionar que la Areacolumna también tiene valores para todo el país. Sin embargo, el país tendrá la abreviatura correspondiente de NaN en la abbrevcolumna y, por lo tanto, se ignorará al trazar el gráfico.

    Ahora que hemos preprocesado nuestros datos, los siguientes pasos son sencillos. Primero, crearemos un diccionario de datos que contiene valores de nuestro conjunto de datos, como se muestra a continuación:

    map_data = dict(type="choropleth",
                locations=df['abbrev'],
                locationmode="USA-states",
                colorscale="Reds",
                text=df['Area'],
                marker=dict(line=dict(color="rgb(255,0,0)", width=2)),
                z=df['2017'],
                colorbar=dict(title="GDP Per Capita - 2017")
               )
    

    Puede ver que en el script anterior, para la locationsclave estamos pasando la abbrevcolumna de nuestro conjunto de datos. Esto significa que la trama geográfica de todos los estados de los Estados Unidos se imprimirá en la pantalla.

    Del mismo modo, para la textclave estamos pasando la columna «Área» que contiene el nombre completo del estado. Finalmente, para la zclave, pasamos el PIB per cápita del año 2017.

    También es importante mencionar que tenemos una nueva clave aquí, es decir marker. Esto se usa para crear una frontera entre diferentes estados. Un valor RGB de 255,0,0significa que el borde será rojo. Un ancho de 2 especifica que el ancho del borde es de 2 píxeles.

    El siguiente paso es crear el diseño de nuestro mapa. El siguiente script hace eso:

    map_layout = dict(title="USA States GDP Per Capita - 2017",
                  geo=dict(scope="usa",
                             showlakes=True,
                             lakecolor="rgb(85,173,240)")
                 )
    

    Observe que en el script anterior pasamos Truepor la showlakesclave, lo que significa que los lagos se mostrarán en el mapa y su color será azul claro como lo especifica el valor RGB de rgb(85,173,240).

    Finalmente, debe crear el objeto gráfico y pasarle los diccionarios de diseño y datos, como hicimos en la sección anterior. Mira el siguiente guión:

    map_actual = go.Figure(data=[map_data], layout=map_layout)
    

    Como último paso, debemos llamar al iplot()método y pasarle nuestro objeto gráfico recién creado:

    Te puede interesar:El módulo de archivo temporal de Python
    iplot(map_actual)
    

    Una vez que ejecute el script anterior, verá un mapa de Estados Unidos con el PIB per cápita. Los estados de color más claro tienen un PIB menor que los de color más oscuro.

    Mapas geográficos del mundo

    En las secciones anteriores, vimos mapas gráficos de Estados Unidos. En esta sección, veremos cómo trazar mapas geográficos del mundo. El proceso sigue siendo más o menos similar. Como primer paso, crearemos un diccionario de datos, seguido del diccionario de diseño y el objeto gráfico. Finalmente, usaremos la iplot()función para trazar la gráfica.

    El conjunto de datos

    El conjunto de datos que vamos a utilizar contiene una población mundial por países para los años 1960-2016. Trazaremos un mapa geográfico del mundo mostrando la población de cada país para el año 2016.

    El conjunto de datos se puede descargar desde este enlace de Kaggle . El conjunto de datos se descargará en formato CSV.

    El siguiente script importa el conjunto de datos y muestra sus primeras cinco filas usando el head()método.

    df = pd.read_csv(r'E:/world_pop.csv')
    df.head()
    

    La siguiente imagen contiene una captura de pantalla de la salida:

    Creemos un diccionario de datos que almacenará la información sobre los datos que queremos graficar del conjunto de datos de población que acabamos de cargar.

    map_data = dict(
            type="choropleth",
            locations=df['Country Code'],
            z=df['2016'],
            text=df['Country'],
            colorbar={'title': 'World Population 2016'},
          )
    

    En el script anterior, puede ver que el único cambio que hicimos fue en la locationsclave; ahora estamos pasando el «Código de país» en lugar de las abreviaturas del estado. Este es un requisito obligatorio para trazar la trama geográfica del mundo. Para la textclave, pasamos los valores de la columna «País» que contiene el nombre completo del país. De manera similar, para la zclave, pasamos los valores de la columna «2016» porque esta es la columna que contiene los valores de población para el año 2016.

    El siguiente paso es crear un diccionario de diseño. Mira el siguiente guión:

    map_layout = dict(
        title="World Population 2016",
        geo=dict(showframe=False)
    )
    

    En el diseño, el único parámetro que cambiamos es la showFrameclave del geodiccionario. Si esta clave se establece en False, el gráfico resultante no contiene un límite.

    A continuación, necesitamos crear un objeto de gráfico Plotly y pasarle tanto los datos como los diccionarios de diseño que creamos, como se muestra a continuación:

    map_actual = go.Figure(data=[map_data], layout=map_layout)
    

    Como último paso, necesitamos llamar a la iplot()función y pasarle el objeto gráfico que acabamos de crear.

    La salida debería verse así:

     

    En la salida, puede ver el mapa de todo el mundo junto con las densidades de población. Puede ver que el mapa de India y China es más oscuro en comparación con los otros países, ya que están mucho más poblados en comparación con los países europeos que están menos poblados. Si pasa el mouse sobre cualquier país, debería ver el nombre del país junto con la población total.

    ¿Busca una guía más detallada sobre Plotly y otras bibliotecas de visualización de Python? Consulte Visualización de datos en Python para obtener un excelente recurso sobre 9 de las bibliotecas más populares que existen, incluidas sus características, fortalezas y matices únicos.

    Conclusión

    Plotly es una biblioteca de Python extremadamente útil para la visualización interactiva de datos. En este artículo, vimos cómo podemos usar Plotly para trazar gráficos básicos como diagramas de dispersión, diagramas de líneas, histogramas y diagramas básicos en 3D. También vimos cómo se puede usar Plotly para trazar parcelas geográficas usando el mapa de coropletas. Como ejemplo, trazamos parcelas geográficas para los Estados Unidos y para todo el mundo.

    Le sugiero que explore la biblioteca de coropletas y trate de practicar la creación de algunas parcelas más geográficas, ya que hay muchas características para explorar. La documentación detallada de los mapas de coropletas está disponible en este enlace: https://plot.ly/python/choropleth-maps/ .

    Te puede interesar:El algoritmo Naive Bayes en Python con Scikit-Learn

     

    Rate this post

    Etiquetas: