Librerira Matplotlib - Python
Librerira Matplotlib - Python
Matplotlib
Importante
Figura
La figura es el elemento base sobre el que se construyen todos los gráficos en matplotlib.
Veamos cómo crearla:
>>>
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
2
Documentación Complementaria de Lectura - Programación 1
>>> type(fig)
matplotlib.figure.Figure
>>> fig
<Figure size 640x480 with 0 Axes>
Podemos observar que la resolución (por defecto) de la figura es de 640x480 píxeles y que
no dispone de ningún eje («0 Axes»).
Importante
El término «axes» hace referencia a un conjunto de ejes. Puede resultar confuso en español
y he decidido asignar el nombre marco cuando haga referencia a «axes».
La resolución final de una figura viene determinada por su altura (height) y anchura
(width) especificadas en pulgadas 2 que, a su vez, se multiplican por los puntos por
pulgada o dpi. Veamos el funcionamiento:
>>>
>>> fig
<Figure size 640x480 with 0 Axes>
Importante
Si utilizamos entornos de desarollo basados en Jupyter, los valores por defecto son
distintos:
Ancho de figura: 6 in
Alto de figura: 4 in
DPI: 75
Resolución: 450x300 px
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
3
Documentación Complementaria de Lectura - Programación 1
Por tanto, cuando creamos una figura podemos modificar los parámetros por defecto para
obtener la resolución deseada:
>>>
>>> fig = plt.figure(figsize=(19.2, 10.8)) # 100 dpi
>>> fig
<Figure size 1920x1080 with 0 Axes>
Si nos interesa que cualquier figura tome unos valores concretos de resolución, podemos
modificar los valores por defecto del entorno. Para ello, matplotlib hace uso de un
diccionario plt.rcParams que contiene los parámetros globales de configuración. Veamos
cómo modificarlo:
>>>
>>> plt.rcParams['figure.figsize']
[6.4, 4.8]
>>> plt.rcParams['figure.dpi']
100.0
>>> fig.get_figwidth()
10.0
>>> fig.get_figheight()
5.0
>>> fig.dpi
300.0
Marcos
Para poder empezar a graficar necesitamos tener, al menos, un marco. Utilizaremos la
función add_subplot() que requiere pasar como parámetros el número de filas, el número
de columnas y el marco activo:
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
4
Documentación Complementaria de Lectura - Programación 1
>>>
>>> fig = plt.figure()
>>> ax
<AxesSubplot:>
>>> fig
<Figure size 640x480 with 1 Axes>
Truco
Suele ser habitual encontrar ax como nombre de variable del «axes» devuelto por la
función add_subplot().
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
5
Documentación Complementaria de Lectura - Programación 1
Nota
Ahora vamos a generar 4 marcos sobre los que fijaremos un título identificativo:
>>>
>>> fig = plt.figure()
>>> fig
<Figure size 640x480 with 4 Axes>
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
6
Documentación Complementaria de Lectura - Programación 1
Matplotlib nos ofrece una forma compacta de crear a la vez tanto la figura como
los marcos que necesitemos.
Para ello utilizaremos la función plt.subplots() que recibe como parámetros el número
de filas y el número de columnas para la disposición de los marcos, y devuelve una tupla
con la figura y los marcos.
>>>
>>> fig, ax = plt.subplots(1, 1)
>>> fig
<Figure size 640x480 with 1 Axes>
>>> ax
<AxesSubplot:>
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
7
Documentación Complementaria de Lectura - Programación 1
Truco
En el siguiente ejemplo creamos una figura con 6 marcos en disposición de 2 filas por 3
columnas:
>>>
>>> fig, ax = plt.subplots(2, 3)
>>> fig
<Figure size 640x480 with 6 Axes>
>>> ax
array([[<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>],
[<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>]], dtype=object)
>>> ax.shape
(2, 3)
Nota
Etiquetas
Dentro de un marco también es posible fijar las etiquetas de los ejes (X e Y). Veamos cómo
hacerlo:
>>>
>>> fig, ax = plt.subplots()
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
8
Documentación Complementaria de Lectura - Programación 1
>>> fig
<Figure size 640x480 with 1 Axes>
Ejes
Un marco (2D) está compuesto por dos ejes: eje X e eje Y. Podemos acceder a cada eje
mediante sendos atributos:
>>>
>>> ax.xaxis
<matplotlib.axis.XAxis at 0x112b34100>
>>> ax.yaxis
<matplotlib.axis.YAxis at 0x112b34850>
REJILLA
En cada eje podemos activar o desactivar la rejilla, así como indicar su estilo.
>>>
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
9
Documentación Complementaria de Lectura - Programación 1
>>> ax.xaxis.grid(True)
>>> ax.yaxis.grid(True)
>>>
>>> ax.grid(True)
Truco
Las funciones de matplotlib que actúan como «interruptores» tienen por defecto el valor
verdadero. En este sentido ax.grid() invocada sin parámetros hace que se muestre la
rejilla. Esto se puede aplicar a muchas otras funciones.
Supongamos ahora que queremos personalizar la rejilla con estilos diferentes en cada eje:
>>>
>>> ax.xaxis.grid(color='r', linestyle='-') # equivale a color='red', linestyle='solid'
>>> ax.yaxis.grid(color='b', linestyle='-') # equivale a color='blue', linestyle='solid'
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
10
Documentación Complementaria de Lectura - Programación 1
MARCAS
Por defecto, los ejes del marco tienen unas marcas 3 equiespaciadas que constituyen
las marcas mayores. Igualmente existen unas marcas menores que, a priori, no están
activadas.
>>>
>>> from matplotlib.ticker import MultipleLocator
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
11
Documentación Complementaria de Lectura - Programación 1
También es posible asignar etiquetas a las marcas menores. En ese sentido, veremos un
ejemplo en el que incorporamos los valores a los ejes con estilos propios:
>>>
>>> # Eje X
>>> ax.xaxis.set_minor_formatter('{x:.1f}')
>>> ax.tick_params(axis='x', which='minor', labelsize=8, labelcolor='gray')
>>> # Eje Y
>>> ax.yaxis.set_minor_formatter('{x:.2f}')
>>> ax.tick_params(axis='y', which='minor', labelsize=8, labelcolor='lightskyblue')
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
12
Documentación Complementaria de Lectura - Programación 1
Primeros pasos
>>>
>>> x = np.linspace(0, 2 * np.pi)
>>> y = np.sin(x)
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
13
Documentación Complementaria de Lectura - Programación 1
Múltiples funciones
Partiendo de un mismo marco, es posible graficar todas las funciones que necesitemos. A
continuación crearemos un marco con las funciones seno y coseno:
>>>
>>> x = np.linspace(0, 2 * np.pi)
>>> sin = np.sin(x)
>>> cos = np.cos(x)
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
14
Documentación Complementaria de Lectura - Programación 1
Nota
Los colores «auto» asignados a las funciones siguen un ciclo establecido por matplotlib que
es igualmente personalizable.
Leyenda
En el caso de que tengamos múltiples gráficos en el mismo marco puede ser deseable
mostrar una leyenda identificativa. Para usarla necesitamos asignar etiquetas a cada
función. Veamos a continuación cómo incorporar una leyenda:
>>>
>>> ax.plot(x, sin, label='sin')
[<matplotlib.lines.Line2D at 0x124e07ac0>]
>>> ax.legend()
<matplotlib.legend.Legend at 0x123c8f190>
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
15
Documentación Complementaria de Lectura - Programación 1
>>>
>>> ax.plot(x, sin, label='$f_1(x) = sin(x)$')
[<matplotlib.lines.Line2D at 0x11682f3a0>]
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
16
Documentación Complementaria de Lectura - Programación 1
UBICACIÓN DE LA LEYENDA
Matplotlib intenta encontrar la mejor ubicación para la leyenda en el marco. Sin embargo,
también es posible personalizar el lugar en el que queremos colocarla.
Si nos interesa situar la leyenda en la parte superior central del marco haríamos lo
siguiente:
>>>
>>> ax.legend(loc='upper center')
<matplotlib.legend.Legend at 0x1167d43a0>
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
17
Documentación Complementaria de Lectura - Programación 1
Aplicando estilos
Para cada función que incluimos en el marco es posible establecer un estilo personalizado
con multitud de parámetros. Veamos la aplicación de algunos de estos parámetros a las
funciones seno y coseno con las que hemos estado trabajando:
>>>
>>> sin_style = dict(linewidth=3, color='darkorange')
>>> cos_style = dict(marker='o', markerfacecolor='limegreen', color='darkgreen')
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
18
Documentación Complementaria de Lectura - Programación 1
Acotando ejes
Hay veces que nos interesa definir los límites de los ejes. En ese caso, podemos hacerlo de
una manera muy sencilla:
>>>
>>> ax.set_xlim(0, np.pi / 2)
>>> ax.set_ylim(0, 1)
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
19
Documentación Complementaria de Lectura - Programación 1
Truco
Anotaciones
En ocasiones necesitamos añadir ciertas anotaciones al gráfico que estamos diseñando. Esto
permite destacar áreas o detalles que pueden ser relevantes.
Partiendo de las funciones seno y coseno con las que hemos estado trabajando, vamos a
suponer que queremos obtener sus puntos de corte, es decir, resolver la siguiente
ecuación:
Para el caso que nos ocupa haríamos n=0�=0 con lo que obtendríamos la siguiente
solución:
>>>
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
20
Documentación Complementaria de Lectura - Programación 1
>>>
>>> ax.annotate('$sin(x) = cos(x)$',
... xy=(xsol, ysol),
... xytext=(1.2, 0.8),
... arrowprops=dict(facecolor='black', shrink=0.05))
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
21
Documentación Complementaria de Lectura - Programación 1
Ejercicio
Datos:
Solución: soften_wave.py
Tipos de gráficos
Mediante matplotlib podemos hacer prácticamente cualquier tipo de gráfico. En esta
sección haremos un repaso por algunos de ellos.
Gráficos de barras
Vamos a partir de un «dataset» que contiene los resultados de los Juegos Olímpicos de
Tokio 2020. Hemos descargado el fichero medals.xlsx desde una página de Kaggle 4.
>>>
>>> df = pd.read_excel('pypi/datascience/files/medals.xlsx')
>>> df.head()
Rank Team/NOC Gold Silver Bronze Total Rank by Total
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
22
Documentación Complementaria de Lectura - Programación 1
>>> df.head()
Rank Gold Silver Bronze Total Rank by Total
Country
United States of America 1 39 41 33 113 1
People's Republic of China 2 38 32 18 88 2
Japan 3 27 14 17 58 5
Great Britain 4 22 21 22 65 4
ROC 5 20 28 23 71 3
Importante
A continuación crearemos un gráfico de barras con las medallas de oro, plata y bronce
de los 10 primeros países ordenados por su ranking. Lo primero será crear el
subconjunto de datos sobre el que vamos a trabajar. Hay muchas maneras de hacerlo. Una
de ellas:
>>>
>>> df_best = df.nsmallest(10, 'Rank')
>>> df_best
Rank Gold Silver Bronze Total Rank by Total
Country
United States of America 1 39 41 33 113 1
People's Republic of China 2 38 32 18 88 2
Japan 3 27 14 17 58 5
Great Britain 4 22 21 22 65 4
ROC 5 20 28 23 71 3
Australia 6 17 7 22 46 6
Netherlands 7 10 12 14 36 9
France 8 10 12 11 33 10
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
23
Documentación Complementaria de Lectura - Programación 1
Germany 9 10 11 16 37 8
Italy 10 10 10 20 40 7
>>>
>>> fig, ax = plt.subplots(figsize=(8, 5), dpi=100) # 800x500 px
>>> ax.set_xticks(x)
>>> ax.set_xticklabels(df_best.index, rotation=90)
>>> ax.legend()
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
24
Documentación Complementaria de Lectura - Programación 1
Ejercicio
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
25
Documentación Complementaria de Lectura - Programación 1
Solución: tiobe_2020.py
Gráficos de dispersión
Para este gráfico vamos a usar un «dataset» de jugadores de la NBA 5 extraído desde esta
página de Kaggle. El fichero nba-data.csv contiene información desde 1996 hasta 2019.
En primer lugar cargamos los datos y nos quedamos con un subconjunto de las columnas:
>>>
>>> df = pd.read_csv('pypi/datascience/files/nba-data.csv', usecols=['pts', 'reb', 'ast'])
>>> df.head()
pts reb ast
0 4.8 4.5 0.5
1 0.3 0.8 0.0
2 4.5 1.6 0.9
3 7.8 4.4 1.4
4 3.7 1.6 0.5
>>> df.shape
(11700, 3)
>>>
>>> fig, ax = plt.subplots(figsize=(8, 6), dpi=100) # 800x600 px
>>> p = ax.scatter(x, y,
... s=30, # tamaño de los puntos
... c=colors, cmap='RdBu_r', # colores
... vmin=colors.min(), vmax=colors.max(), # normalización de colores
... alpha=0.7,
... edgecolors='none')
>>> cb.outline.set_visible(False)
>>> ax.set_xlabel('Puntos')
>>> ax.set_ylabel('Rebotes')
>>> ax.spines['right'].set_visible(False)
>>> ax.spines['top'].set_visible(False)
>>> fig.tight_layout()
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
27
Documentación Complementaria de Lectura - Programación 1
Ejercicio
Solución: bmw_plot.py
Histogramas
En esta ocasión vamos a trabajar con un «dataset» de «Avengers» 6 extraído desde Kaggle.
Hemos descargado el fichero avengers.csv.
>>>
>>> df = pd.read_csv('pypi/datascience/files/avengers.csv', usecols=['Year'])
>>> df.head()
Year
0 1963
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
28
Documentación Complementaria de Lectura - Programación 1
1 1963
2 1963
3 1963
4 1963
>>> df.shape
(173, 1)
Igualmente haremos un pequeño filtrado para manejar sólo registros a partir de 1960:
>>>
>>> df = df[df['Year'] >= 1960]
>>> df.shape
(159, 1)
>>>
>>> df['Year'].min(), df['Year'].max()
(1963, 2015)
>>>
>>> fig, ax = plt.subplots(figsize=(8, 4), dpi=100) # 800x400 px
>>> ax.hist(df,
... bins=bins, # intervalos de agrupación
... rwidth=0.95, # ancho de cada barra
... zorder=2, # barras por encima de rejilla
... color='deeppink',
... alpha=0.5)
>>> ax.spines['right'].set_visible(False)
>>> ax.spines['top'].set_visible(False)
>>> fig.tight_layout()
Ejercicio
Partiendo del fichero pokemon.csv que contiene información sobre Pokemon 10, cree el
siguiente histograma en el que se analiza el número de personajes «pokemons» en función
de su velocidad (columna Speed):
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
30
Documentación Complementaria de Lectura - Programación 1
Solución: pokemon_speed.py
Vamos a trabajar con un conjunto de datos extraído desde esta página de Kaggle que
contiene información histórica de temperaturas del planeta Tierra. El fichero global-
temperatures.csv se ha descargado para su tratamiento.
En primer lugar cargamos los datos, renombramos las columnas y eliminamos los valores
nulos:
>>>
>>> df = pd.read_csv('pypi/datascience/files/global-temperatures.csv',
... parse_dates=['dt'], # conversión a tipo datetime
... usecols=['dt', 'LandAverageTemperature'])
>>> df.head()
when temp
0 1750-01-01 3.034
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
31
Documentación Complementaria de Lectura - Programación 1
1 1750-02-01 3.083
2 1750-03-01 5.626
3 1750-04-01 8.490
4 1750-05-01 11.573
>>> df.shape
(3180, 2)
>>>
>>> # Necesitamos algunas utilidades de gestión de fechas
>>> from matplotlib.dates import YearLocator, DateFormatter, date2num
>>> from matplotlib.ticker import MultipleLocator
>>> ax.plot(x, y,
... linestyle='None', marker='.', color='tomato', # estilo de línea
... zorder=2) # orden para colocar sobre rejilla
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
32
Documentación Complementaria de Lectura - Programación 1
>>> ax.spines['right'].set_visible(False)
>>> ax.spines['top'].set_visible(False)
>>> fig.tight_layout()
Mapas de calor
Para este tipo de gráfico vamos a utilizar un «dataset» que recoge las 1000 películas más
valoradas en IMDB 7. Está sacado desde esta página de Kaggle y se ha descargado el
fichero de datos en imdb-top-1000.csv.
>>>
>>> df = pd.read_csv('pypi/datascience/files/imdb-top-1000.csv',
... usecols=['Certificate', 'Genre', 'IMDB_Rating'])
>>> df.head()
Certificate Genre IMDB_Rating
0 A Drama 9.3
1 A Crime, Drama 9.2
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
33
Documentación Complementaria de Lectura - Programación 1
>>>
>>> df['Main_Genre'] = df['Genre'].str.split(',', expand=True)[0]
>>> df.head()
Certificate Genre IMDB_Rating Main_Genre
0 A Drama 9.3 Drama
1 A Crime, Drama 9.2 Crime
2 UA Action, Crime, Drama 9.0 Action
3 A Crime, Drama 9.0 Crime
4 U Crime, Drama 9.0 Crime
>>>
>>> # unstack permite disponer la agrupación en forma tabular (para el heatmap)
>>> ratings = df.groupby(['Certificate', 'Main_Genre'])['IMDB_Rating'].mean().unstack()
>>> ratings
Main_Genre Animation Action Adventure Biography Comedy Crime Drama
Certificate
ALL 7.947368 8.165000 7.953571 7.862500 7.940541 8.200000
7.976364
>12 7.883333 7.992424 7.958333 7.971429 7.885714 7.900000
7.953659
>13 7.866667 7.783333 7.600000 7.862500 7.785714 8.000000
7.775000
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
34
Documentación Complementaria de Lectura - Programación 1
>>>
>>> fig, ax = plt.subplots(figsize=(8, 4), dpi=100)
>>> x = ratings.columns
>>> y = ratings.index
>>> # Mostrar las etiquetas. El color del texto cambia en función de su normalización
>>> for i in range(len(y)):
... for j in range(len(x)):
... value = ratings.iloc[i, j]
... text_color = text_colors[int(im.norm(value) > 0.5)] # color etiqueta
... ax.text(j, i, f'{value:.2f}', color=text_color, va='center', ha='center')
>>> ax.spines[:].set_visible(False)
>>> fig.tight_layout()
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
35
Documentación Complementaria de Lectura - Programación 1
Ejercicio
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
36
Documentación Complementaria de Lectura - Programación 1
Solución: euro_dollar.py
Diagramas de caja
Para mostrar el funcionamiento de los diagramas de caja en Matplotlib vamos a hacer uso
de distintas distribuciones aleatorias que crearemos mediante funciones de Numpy:
>>>
>>> DIST_SIZE = 100 # tamaño de la muestra
>>> boxplots = []
>>> boxplots.append(dict(
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
37
Documentación Complementaria de Lectura - Programación 1
>>> boxplots.append(dict(
... dist=np.random.geometric(0.4, size=DIST_SIZE),
... label='Geometric\n$p=0.4$',
... fill_color='lightblue',
... brush_color='navy'))
>>> boxplots.append(dict(
... dist=np.random.chisquare(2, size=DIST_SIZE),
... label='Chi-squared\n$df=2$',
... fill_color='lightgreen',
... brush_color='darkgreen'))
Ahora ya podemos construir el gráfico de cajas que nos permite visualizar la distribución de
las muestras:
>>>
>>> fig, ax = plt.subplots(figsize=(8, 6), dpi=100) # 800x600 px
... linewidth=1))
>>> ax.yaxis.grid(color='lightgray')
>>> ax.xaxis.set_ticks_position('none')
>>> ax.yaxis.set_ticks_position('none')
>>> ax.spines[:].set_visible(False)
>>> fig.tight_layout()
Consejo
Gráficos de evolución
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
39
Documentación Complementaria de Lectura - Programación 1
>>>
>>> import datetime
>>> df = pd.read_csv('pypi/datascience/files/eth-usd.csv',
... parse_dates=['Date'],
... usecols=['Date', 'Open', 'Volume'],
... index_col='Date')
>>> df_smooth.head()
Open Volume
Date
2017-01-21 9.968611 2.146882
2017-01-22 10.105573 2.117377
2017-01-23 10.222339 1.985587
2017-01-24 10.273270 1.821968
2017-01-25 10.239854 1.647938
>>>
>>> fig, ax = plt.subplots(figsize=(8, 4), dpi=100) # 800x400px
>>> # Rejilla
>>> ax.xaxis.grid(color='lightgray', linewidth=.5)
>>> for y_tick in y_ticks:
... if y_tick != 0:
... ax.axhline(y_tick, color='lightgray', linewidth=.5)
>>> ax.legend()
>>> ax.spines[:].set_visible(False)
>>> fig.tight_layout()
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis
41
Documentación Complementaria de Lectura - Programación 1
Ejercicio
Solución: mwh_spain.py
Tecnicatura Superior en Desarrollo de Software - Prof. Analista de Sist.: Pablo Sanchez Andrulakis