Python para Estadística: Guía Práctica
Python para Estadística: Guía Práctica
Programación en Python:
Estadística a través
de problemas resueltos
texto:
Manuel José Martínez-Santaolalla Martínez
Isabel María del Águila Cano
edición:
Editorial Universidad de Almería, 2025
editorial@[Link]
[Link]/editorial
Telf/Fax: 950 015459
¤
ISBN: 978-84-1351-388-1
Prefacio 1
Introducción a la Programación 3
Conceptos básicos de la informática . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Ejecución de programas. Lenguajes de Programación. . . . . . . . . . . . . . . . . . 9
Instrucciones básicas 17
Identificadores y variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Primer ejemplo en Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Tipos de datos simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Tipos de datos compuestos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Instrucción de asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Instruccion de entrada y salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Funciones integradas e importadas . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
IIi
Programación en Python: Estadística a través de problemas resueltos
iV
Prefacio
1
Programación en Python: Estadística a través de problemas resueltos
2
Introducción a la Programación
Aunque no es estrictamente necesario para aprender a programar, es útil comprender qué significa
realmente la computación o la programación, así como conocer los componentes y el funcionamiento
básico de una computadora. Por ello, antes de adentrarse en la construcción de programas
informáticos, este libro resume brevemente estos conceptos para luego pasar a los fundamentos
propios de la programación de computadoras usando Python.
En literatura anglosajona nos encontramos con los términos: Computer Science (ciencia de los
computadores), Computer Engineering o IT Engineering. En literatura francófona: Informatique.
3
Programación en Python: Estadística a través de problemas resueltos
Máquinas programables
Una máquina es un cierto dispositivo físico capaz de realizar un cierto trabajo u operación, bien
de forma manual (una garrucha) o bien de forma automática (un ascensor).
Este concepto puede extenderse a cuando se consideran máquinas que no existen físicamente, pero
en las que puede describirse, concebirse y simular su comportamiento, se denominan máquinas
virtuales.
En el caso del ascensor su funcionamiento está fijado en el momento de su construcción dependiendo
de los dispositivos y conexiones entre ellos.
Existen otras máquinas automáticas (que actúan por si solas) denominadas máquinas pro-
gramables, cuyo comportamiento fijo se completa con un programa que permite modificar el
comportamiento de la máquina base. Tal como se muestra en la siguiente figura, basta con cambiar
el programa para tener una nueva máquina con un comportamiento diferente.
Concepto de cómputo
La definición de diccionario de cómputo indica que es el cálculo destinado a obtener el resultado o
valor de algo.
Por lo tanto, un cómputo implica el procesamiento de información, que tradicionalmente es
numérica, aunque puede extenderse a otro tipo de información como texto o sonido.
Cualquier cómputo se puede describir de diferentes maneras. Por ejemplo, el cómputo para
Calcular la nota final de un alumno que ha realizado tres exámenes a lo largo del curso, pero
sabiendo que si en uno de los exámenes tiene un cero, entonces su nota es cero podría hacerse de
dos formas:
Una opción es utilizar la siguiente fórmula
√
N ota ≈ 3 nota1 ∗ nota2 ∗ nota3
4
Programación en Python: Estadística a través de problemas resueltos
1. Nota = 0;
2. Si nota1 no es cero Nota = Nota + nota1
- Sino Nota = 0 e ir a paso 5
3. Si nota2 no es cero Nota = Nota + nota2
- Sino Nota = 0 e ir a paso 5
4. Si nota3 no es cero Nota = Nota + nota3
- Sino Nota = 0 e ir a paso 5
5. Nota = Nota / 3
Concepto de computador
Un computador es una máquina programable para el tratamiento de información, es decir realiza
cómputos. Posee unos elementos fijos o máquina de base llamada hardware y otros modificables
que son los programas o software.
Los computadores actuales son máquinas con programa almacenado de forma que la modificación
de los programas no implica la modificación de los elementos físicos, y por tanto, se necesitan
sistemas de almacenamiento y de comunicación del ordenador con el entorno.
Las máquinas virtuales en la nube son entornos computacionales virtuales que se ejecutan en
infraestructuras de computación remota y están disponibles a través de Internet. Estas máquinas
virtuales proporcionan capacidades informáticas similares a las de una computadora física, pero
con la flexibilidad y escalabilidad adicionales ofrecidas por la computación en la nube. Por tanto,
más que hablar de elementos electrónicos se tiene que hablar de unidades funcionales, ya que
no tienen porque existir asignadas físicamente a una máquina y que son redimensionables. Se
definen o alquilan componentes/unidades lógicas sobre servidores remotos para crear computadoras
personalizadas virtuales. Es decir, la máquina base se define de forma virtual.
5
Programación en Python: Estadística a través de problemas resueltos
Unidad de entrada
Esta unidad obtiene la información (datos y programas informáticos) de los dispositivos de entrada
y la pone a disposición de las demás unidades para su procesamiento. Los computadores reciben la
mayoría de las entradas de los usuarios a través de teclados, pantallas táctiles, ratones y touchpads,
aunque también pueden utilizar otros dispositivos como escáneres, micrófonos, cámaras web,
sensores biométricos y lectores de códigos de barras.
Unidad de salida
Esta unidad envía la información que el computador ha procesado a uno o varios dispositivos de
salida para que esté disponible fuera del ámbito del computador, y para ser usada directamente por
el usuario o bien por otro computador. La mayor parte de la información sale de los ordenadores
a través de las pantallas o se reproduce en audio o vídeo sobre teléfonos inteligentes, tabletas,
ordenadores y pantallas gigantes en estadios deportivos, o muy habitualmente se transmite por
Internet. Otras formas de salida son la vibración de teléfonos inteligentes, mandos de juegos y
dispositivos de realidad virtual.
Unidad de memoria
Esta unidad es un almacén de acceso rápido y capacidad relativamente baja que mantiene la
información introducida a través de la unidad de entrada, haciéndola disponible cuando sea
necesario. La unidad de memoria también guarda la información procesada hasta que puede ser
colocada en la unidad de salida.
6
Programación en Python: Estadística a través de problemas resueltos
Esta unidad fabrica datos, es decir, realiza cálculos (suma, resta, multiplicación y división) y toma
decisiones (por ejemplo, comparar dos elementos de la unidad de memoria para determinar si son
iguales). En los sistemas actuales, la ALU forma parte de la siguiente unidad lógica.
Esta unidad es la que coordina y supervisa el funcionamiento de las demás unidades. La unidad
central de proceso (CPU) indica:
La mayoría de los ordenadores actuales tienen procesadores multinúcleo que implementan de forma
económica múltiples procesadores en un único chip de circuito integrado. Estos procesadores
pueden realizar muchas operaciones simultáneamente. Un procesador de doble núcleo tiene dos
CPU, un procesador octa-core tiene ocho. Intel tiene algunos procesadores con hasta 72 núcleos.
Entre los dispositivos de almacenamiento secundario más comunes se encuentran las unidades de
estado sólido (SSD), las memorias flash USB o lápices de memoria, los discos duros mecánicos
(HDD), así como también las unidades ópticas como los discos Blu-Ray que permiten lectura y
escritura de datos.
7
Programación en Python: Estadística a través de problemas resueltos
Representación de la información
De las definiciones de dato, según la RAE, son del contexto de la computación la 1 y la 3, en
concreto la tercera solamente es relativa a la informática.
1. m. Información sobre algo concreto que permite su conocimiento exacto o sirve para
deducir las consecuencias derivadas de un hecho. A este problema le faltan datos
numéricos.
2. m. Documento, testimonio, fundamento.
3. m. Inform. Información dispuesta de manera adecuada para su tratamiento por una
computadora.
Más específicamente un dato en informática es: una representación formalizada de hechos o
conceptos susceptible de ser comunicada o procesada.
Existen diversos tipos de datos, y por tanto, su representación es diferente:
Numéricos (12, 28.5): reales o enteros
Alfabéticos (Ana)
Alfanuméricos: 23456X, M-6995
Imágenes, sonido, video.
Se llama representación de la información a la forma en la que se almacenan y recogen los
datos para poder ser procesados en la computadora. Se debe tener en cuenta que en los medios
electrónicos de procesamiento solamente disponen de dos estados, interruptor abierto-cerrado.
Esto se traslada a que la información procesada por un computador son siempre unos y ceros. Lo
que lleva a la necesidad de traducir cualquier dato a una combinación de esos dos símbolos es
decir notación binaria o sistema de numeración binario.
Nº decimal 0 1 2 3 4 5 6 7
Nº binario 0 1 10 11 100 101 110 111
Observando la tabla se comprueba que, por ejemplo, para representar el “6” en decimal son
necesarias tres posiciones en binario. Cada una de estas posiciones es un bit, siendo la unidad más
pequeña de información un uno o un cero. Cabe decir que en la computación cuántica se amplía
este concepto incorporando la idea de que cada bit puede ser cero, uno, o no conocerse su valor.
Veáse un ejemplo: representar en binario todos los posibles estados emocionales de una persona.
Estados = { Tranquilo, Feliz, Emocionado, Preocupado, Triste, Enojado, Sorprendido, Nervioso,
Cansado, Motivado }
8
Programación en Python: Estadística a través de problemas resueltos
Son necesarias cuatro cifras binarias. 24 es el máximo número de elementos a representar, en este
caso, ejemplo hay 10 estados.
Un byte es un conjunto de 8 bits. También se llama Octeto o Carácter (porque con un byte se
codifica un carácter). Ésta es la unidad de almacenamiento de información en informática (por
ejemplo, tamaño de memoria o tamaño de almacenamiento secundario) que siempre se mide en
potencias de dos.
Los qubits (cúbit) pueden representar simultáneamente 0 y 1. El estado de un qubit puede depender
del estado de otro, lo que proporciona una mayor capacidad de procesamiento y almacenamiento
de información.
Múltiplos del byte
9
Programación en Python: Estadística a través de problemas resueltos
En general cualquier software es una lista de instrucciones máquina que al ejecutarse producen un
resultado concreto. Deben estar expresados en un lenguaje que entienda la máquina, o bien los
programadores deben escribirlos en algún lenguaje de más alto nivel (lenguaje de programación)
que ha de ser traducido a lenguaje máquina utilizando algún tipo de software de base como son
los compiladores o los intérpretes.
Todo programa comienza con idea, algo que se quiere hacer. Generalmente ese algo se plantea
como solución a un problema específico. La elaboración de esta solución requiere el diseño de un
algoritmo.
Los algoritmos son soluciones abstractas a problemas, generalmente son codificados posterioremente
en un lenguaje de programación, y luego se traducen al lenguaje máquina que es el único que una
computadora puede ejecutar. Con sus resultados, este algoritmo en forma de programa solucionará
el problema real para el que se concibió. Los algoritmos son independientes del lenguaje de
programación y de la máquina que lo ejecute.
Una analogía de la vida real sería cuando un cocinero con vista cansada quiere hacer una receta. La
receta de un plato de cocina (algoritmo) puede expresarse en inglés, francés o español (lenguaje),
e indicará la misma preparación independientemente del cocinero (máquina). Después, el cocinero
concreto que lea la receta, debe poder entender lo escrito y como el cocinero es corto de vista,
necesita sus gafas (compilador) para que le traduzcan los garabatos a letras. Sin gafas el cocinero
solamente ve garabatos y necesita que algo se los traduzca (gafas).
10
Programación en Python: Estadística a través de problemas resueltos
11
Programación en Python: Estadística a través de problemas resueltos
Intérprete: el programa fuente se traduce instrucción a instrucción cada vez que se ejecuta
(no se crea el programa objeto).
Se diseñó para ser un lenguaje muy legible, utilizando palabras clave en inglés donde otros
lenguajes usan signos de puntuación, facilitando la comprensión y mantenimiento del código.
Multiparadigma soportando por ejemplo la programación orientada a objetos, la programación
imperativa y, en menor medida, la programación funcional.
Multiplataforma porque es compatible con múltiples sistemas operativos, como Windows,
macOS y diversas distribuciones de Linux, permitiendo su ejecución en diferentes entornos
sin necesidad de modificaciones en el programa.
Cuenta con una gran cantidad de librerías y paquetes que extienden sus funcionalidades.
Una librería es un conjunto de módulos que ofrecen herramientas reutilizables para diversas
tareas, como matemáticas, manipulación de datos, desarrollo web, inteligencia artificial,
entre otras. Ejemplos destacados son Numpy para cálculo numérico y Pandas para análisis
de datos.
Interpretado: se requiere tener Python disponible para ejecutar las instrucciones.
12
Programación en Python: Estadística a través de problemas resueltos
Java
Hola Mundo
Un programa es por tanto un conjunto de “texto” o bloque de código escrito con unas normas
de sintaxis marcadas por el lenguaje de programación que busca hacer algo y que se guarda en un
archivo plano (.c, .py, .java). Sea el siguiente ejemplo:
Solución: Cada lenguaje tiene una forma de expresarlo. Nótese que cada lenguaje tiene su forma
de organizar los bloques de código e incorporar comentarios. Por ejemplo, en C se utiliza /* y
*/, en Java se utiliza // para los comentarios y en el caso de Python se usa #.
Solución en ensamblador
section .data
msg db "Hola Mundo!", 0ah
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, 13
syscall
mov rax, 60
mov rdi, 0
syscall
Solución en C
int main() {
printf("Hola Mundo!\n");
return 0;
}
13
Programación en Python: Estadística a través de problemas resueltos
Solución en Java
Solución en Python
Pero ¿cómo se ejecuta este código? Tal como ya se ha dicho, algunos programas tienen que ser
compilados y otros interpretados por un traductor del lenguaje concreto. En en el caso de C, se
debe compilar el archivo que contiene el código para obtener un archivo ejecutable que se podrá
usar sin que el compilador de C esté instalado en la misma máquina. Sin embargo, en Python,
bien sea teniendo el código en un archivo .py o bien escribiendo directamente las instrucciones,
para poder “saludar” es imprescindible tener un intérprete Python disponible en la máquina donde
se ejecute.
El código Python (siempre con el intérprete presente) se ejecuta de diversas formas. Tres de los
modos básicos de uso de Python son el modo interactivo, el modo script y los cuadernos.
En el modo interactivo, introduciendo directamente fragmentos de código Python viendo sus
resultados inmediatamente. El símbolo >>> indica que el intérprete está cargado.
En el modo script, se ejecuta el código cargando desde un archivo con la extensión .py que lo
contiene, son los llamados scripts, que podrían traducirse como secuencia de instrucciones, aunque
no suele hacerse y el nombre en inglés es el más utilizado. El código, en modo texto, se coloca en
un archivo llamándolo por ejemplo [Link]. Este archivo se puede lanzar bien desde entornos
de programación como Visual Studio Code, Anaconda o WinPython o directamente desde la
consola llamando al intérprete, python [Link].
14
Programación en Python: Estadística a través de problemas resueltos
15
Programación en Python: Estadística a través de problemas resueltos
Finalmente, sobre todo para soluciones finalistas, se pueden ejecutar fragmentos (chunks o snippets)
de código Python sobre cuadernos Jupyter (Jupyter Notebook), en especial sobre Colab de
Google, que es un servicio de alojamiento de cuadernos que no requiere configuración y que
ofrece acceso sin coste a recursos de computación. Los cuadernos permiten combinar elementos
de texto enriquecido (formato, tablas, figuras, ecuaciones, enlaces, etc.), código y el resultado de
ejecutar el código en un único documento.
16
Instrucciones básicas
Un programa escrito en un lenguaje de alto nivel son varias instrucciones (órdenes) agrupadas,
los grupos son llamados bloques de código y bloques de código con nombre (similar a una
función matemática). Las instrucciones son las unidades elementales de un bloque y se utilizan
para especificar las tareas que el programa debe llevar a cabo utilizando los datos guardados en
la memoria. Los programas simples suelen tener un único bloque de código.
En el ejemplo “Hola Mundo” en C hay un bloque de código llamado main, mientras que en el
ensamblador hay tres secciones identificadas por un tabulador y un nombre, mientras que en
Python solamente se tiene una línea de código.
Cada instrucción en un lenguaje de programación realiza una tarea particular, que se categorizan
en tres grupos:
Asignación
Entrada / Salida
De control
Las instrucciones necesitan información para trabajar. Por ejemplo, si se quieren sumar dos
números, esos números son los datos necesarios para ejecutar la instrucción +. De igual forma,
en una instrucción print usada para imprimir en pantalla un texto, la cadena incluida entre
paréntesis es el dato necesario para ejecutar la instrucción (por ejemplo, “Hola”).
17
Programación en Python: Estadística a través de problemas resueltos
trasladar el algoritmo a un programa fuente en un lenguaje para dar las órdenes a ejecutar una
vez traducido el fuente sobre la máquina.
Realmente un lenguaje de programación es un idioma acotado a las cosas que el programador
puede decir para que las entienda un computador y que vienen heredadas del inglés, algunas son:
print input
if elif else
while for break continue
True False None and
import def
Para Python todas las palabras clave del lenguaje pueden verse si se ejecuta en el intérprete las
líneas:
import keyword
print([Link])
Identificadores y variables
Todo programa utiliza datos para poder realizar la tarea que tenga asignada, bien sea almacenán-
dolos, modificándolos o mostrándolos. Los datos pueden ser números, caracteres u otros tipos que
se verán después. Atendiendo a su uso estos datos se clasifican en:
Constante literal: cualquier valor constante escrito directamente en un programa. Si se
escribe x + 3, el número 3 es una constante literal; Hola Mundo también es una constante
de cadena.
Constante simbólica (con nombre): valor de dato constante que se referencia mediante
un nombre (identificador). Pero en Python no se declaran constantes como tales, realmente
son variables. Por convenio se suelen poner en mayúsculas pero pueden ser reasignadas, por
ejemplo: PI = 3.1416.
Variable: zona de memoria central que se referencia mediante un nombre o identificador,
en lugar de conocer la dirección física donde está en memoria, para usarla se usa el nombre
que se le ha asignado. En una variable se almacena el valor de un dato que puede cambiar
durante la ejecución del programa.
Cada lenguaje maneja las variables y las constates de forma diferente. En C/C++ se usa tipado
estático, es decir, el tipo de cada variable debe declarase antes de usarlo (sino se produce un
error) y los nombres hacen referencia permanente a localizaciones o bloques de memoria, no
18
Programación en Python: Estadística a través de problemas resueltos
cambian durante el ámbito de uso de la variable. Cada uno de estos bloques tiene una capacidad
de almacenar información que depende de la representación interna del tipo de dato de la variable.
Python es un lenguaje de tipado dinámico, donde no hay que declarar el tipo de las variables antes
de usarlas, según el uso se le asigna un tipo, que además puede cambiar en la ejecución.
Una variable se puede ver como una caja dentro de la memoria, donde se almacena el valor de
un dato, y que es conocida en el programa mediante el nombre que se ha dado a la caja (o
identificador).
Las cajas de las variables pueden ser de distintos tamaños, en función del tipo de dato que se
pueda almacenar en ellas. Así, si la variable va a contener valores de tipo carácter, su tamaño
será de un byte. Si va a contener valores de tipo entero int, su tamaño será de dos bytes. Si
va a contener una cadena de varios caracteres, necesitará ser de un tamaño igual al número de
caracteres más uno (porque las cadenas añaden al final un carácter especial fin de cadena).
Hay lenguajes, como C, en los que las variables han de declararse indicando el tipo de dato de
los valores que van a contener. La razón es que hay que reservar los espacios en memoria de todas
las variables antes de comenzar la ejecución del programa, en Python no es necesario puesto que
esta reserva se hace de forma dinámica.
Las variables en Python simplemente se definen al ser utilizadas por primera vez. Además, (si bien
no es recomendable) pueden cambiar de tipo si se realiza una asignación con otro tipo:
x = "hola"
print(x)
x = 5
print(x)
o bien,
y = x + 2.5
print(y)
19
Programación en Python: Estadística a través de problemas resueltos
Para Python las normas a cumplir para construir los identificadores son:
# Válido
_variable = 10
vari_able = 20
variable10 = 30
variable = 60
variaBle = 10
Variable = 20
CONSTANTE = 0
# No válido
2variable = 10
var-iable = 10
var iable = 10
Algo que distingue a Python de otros lenguajes es que es interpretado, no compilado. Esto
significa que se ejecuta línea a línea, lo que permite que la programación sea interactiva de una
forma que no es directamente posible con lenguajes compilados.
Cuando se juntan varias instrucciones para ser ejecutados como una unidad se llama script, que
puede o no estar en un archivo .py. Un script simple, pero que ilustra varios de los aspectos
importantes de la sintaxis de Python es:
20
Programación en Python: Estadística a través de problemas resueltos
Se le indica al intérprete que utilice los datos "Hola" para ejecutar la funcionalidad definida en
print, estos datos que necesita print se llaman argumentos y se colocan entre paréntesis. A
esto se le llama pasar argumentos a print.
Si bien este bloque de dos instrucciones es directamente ejecutable en el intérprete Python, si se
quiere poder utilizar este trabajo de forma repetida debe asignársele un identificador para usarlo
en otras instrucciones. Suele guardase en un archivo .py. Cuando se quiere dar nombre a un
bloque se utilizará la palabra clave def (definir), seguida por un identificador válido:
Los comentarios en Python se indican con la almohadilla (#). Desde la posición de la almohadilla
hasta el final de línea el contenido es ignorado por el intérprete.
print ("Hola", end=" ") # sustituye salto de linea final por un espacio
También hay comentarios multilínea, que se marcan con triples comillas bien sean simples ”’ o
dobles “““, que además de documentar los bloques de código se usan en las utilidades de ayuda:
También es erróneo:
21
Programación en Python: Estadística a través de problemas resueltos
Para que una instrucción continúe en la línea siguiente hay que utilizar el símbolo \. Solamente se
permite la continuación implícita de líneas dentro de paréntesis (), corchetes [] y llaves {} para
mejorar la legibilidad:
print("¿cómo \
estás?")
Si bien es posible poner más de una instrucción en la misma línea, las normas de estilo no lo
recomiendan y debe evitarse:
def saludo():
print ("Hola", end=" ")
print ("como estas?")
Las dos instrucciones print están fuera de saludo, debe estar un tabulador indentado para que
sean parte del bloque de código con nombre.
Ejecución del script
Con def se define un bloque de código con nombre o función pero no se ejecuta, solo se define.
Para ejecutarla se debe llamar al bloque. De esta forma se separa la definición de la ejecución:
def saludo():
print ("Hola", end=" ")
print ("¿cómo estas?")
saludo()
Si se pone:
saludo()
saludo()
Para scripts con un solo bloque no es necesario usar def, es decir, no se le asigna un nombre. Su
nombre es el del archivo .py donde esté.
La decisión de si definir un bloque de código con nombre o no, es decir usar def, dependerá del
uso futuro de ese bloque. Por tanto, si se va a reutilizar desde otros scripts se le dará nombre,
para poder lanzar su ejecución.
22
Programación en Python: Estadística a través de problemas resueltos
Mientras que un dato es el elemento de información concreto, que es objeto de operación por
parte de la computadora y que puede ser simple o compuesto (formado por otros datos), un tipo
de dato es la propiedad asociada a la representación del dato en la computadora. A continuación,
se revisará como Python implanta y define estos tipos.
Cada tipo se caracteriza por la representación interna del tipo, que define el rango de valores
permitidos, y por las operaciones habilitadas para esos tipos. Por ejemplo, dado el conjunto de
enteros {. . . ,-3,-2,-1,0,1,2,3,. . . }, cada valor del tipo se representará en binario en memoria, por
tanto, la física de las celdas de memoria limita el tamaño máximo y mínimo del entero que puede
manejarse para el tipo entero.
En Python, las limitaciones de la representación interna de los datos son menores que en otros
lenguajes puesto que todos los tipos de datos son dinámicos, como se verá más tarde. En el caso
del C estándar un entero debe estar en el rango -32767 a 32767. Las operaciones permitidas para
esos valores enteros son: suma (+), resta (-), división (/), división entera, multiplicación, resto
de la división entera o módulo, exponenciación y cambio de signo.
Se definen tres grandes tipos de datos: datos simples, compuestos y definidos por el usuario.
Datos simples: (indivisible o atómico): Son aquellos que no están compuestos de otros datos
y se distingue entre estándar y no estándar.
• Numéricos:
◦ Enteros – el número de tiradas de un dado.
◦ Reales – la media de las notas de un alumno.
• Lógico o booleano – Si se es o no familia numerosa.
• Carácter – La letra del DNI.
• Punteros, no es un dato en si mismo, es una dirección de memoria (un índice que
referencia una celda física de la memoria). No todos los lenguajes permiten manejar
las direcciones de memoria, curiosamente en Python todo son punteros, aunque sea
trasparente para el usuario principiante.
23
Programación en Python: Estadística a través de problemas resueltos
24
Programación en Python: Estadística a través de problemas resueltos
Tipo entero
La cantidad y el rango de los números enteros y reales en matemáticas es un conjunto infinito.
Una computadora, por contra, dispone de un número finito de posiciones de memoria en las que
almacenar bits para permitir la representación exacta.
En lenguajes como C/C++, de forma nativa, hay un tamaño máximo de un valor entero. En
Python el tipo entero (int), no tiene un límite predeterminado para el máximo valor absoluto. Los
enteros de C son de precisión fija, y normalmente se desbordan en algún valor (a menudo cerca
de 231 o 263 , dependiendo de tu sistema). El tipo int en Python es de precisión variable, por lo
que se pueden hacer cálculos que se desbordarían en otros lenguajes como:
2 ** 200
Otra característica conveniente de los enteros de Python es que, por defecto, la división se convierte
al tipo de punto flotante, es decir, un real. Incluso se pueden especificar en diferentes bases.
Cualquier número sin punto decimal es un entero:
c = 0x7fa8 # Hexadecimal
d = 0o253 # Octal
e = 0b10001111 # Binario
25
Programación en Python: Estadística a través de problemas resueltos
Tipo real
Para los números reales, se utiliza una representación muy diferente a la de los enteros, denominada
de punto (o coma) flotante que está definida en el estándar IEEE 754. La representación de
números reales en las computadoras es limitada: no es posible expresar números infinitamente
grandes, la precisión es finita, por lo que solo una cantidad limitada de números reales puede
representarse exactamente.
Para los tipos float se pueden utilizar la notación decimal o exponencial. El caso de 1.4e6 se
interpreta como 1,4 × 106 .
Un real almacena entre 15 y 17 dígitos con un exponente entre -308 to 308,
x = 0.000005
y = 5e-6
# Estos dos números son iguales
b = 1.8 # flotante
c = a + b # c es 2.8, tipo float
a = 1.8 # flotante
b = int(a) # b es 1, tipo int
a = 1 # entero
b = 2.0 # flotante
c = a + b # c es 3.0, tipo float
Tipo lógico
Recoge la verdad o no de una situación o hecho. Los valores booleanos o lógicos se llaman así en
honor al lógico inglés George Boole. Pueden tomar dos valores: True y False (verdadero, 1, <>
0 y falso, 0). Las operaciones asociadas a estos tipos de datos son los operadores lógicos(and, or,
not) y relacionales (mayor, menor, igual, distinto), cuyo funcionamiento se describirá tarde.
Tipo carácter
Los computadores son conocidos por su capacidad para trabajar con valores numéricos. Sin embargo,
el manejo de textos es una tarea recurrente (procesadores de texto, sistemas de mensajería, etc.).
En todos estos casos, las cadenas de caracteres juegan un papel fundamental.
Si bien se puede trabajar con caracteres individuales, en la práctica, el manejo de textos se realiza
principalmente mediante cadenas de caracteres. Éstas constituyen un tipo de dato compuesto en
el que cada elemento es un carácter, como se verá más adelante.
26
Programación en Python: Estadística a través de problemas resueltos
Los códigos de escape (escape codes) son expresiones que comienzan con una barra invertida,
\ y se usan para representar caracteres especiales que no pueden ser fácilmente tecleados o que
tienen un significado especial dentro de una cadena de texto. Estos códigos permiten, por ejemplo,
insertar saltos de línea, tabulaciones o caracteres de comillas sin interferir con la sintaxis del
lenguaje.
Tipo complejo
Los números complejos (complex) son números con dos elementos, la parte real y la parte
imaginaria (ambas en coma flotante). Para crear un complejo en Python:
complex(1, 2)
Se puede utilizar el sufijo ‘j’ (representa la unidad imaginaria, que es igual a la raíz cuadrada de
-1) en las expresiones para indicar la parte imaginaria. El acceso a las partes real e imaginaria
puede verse en el siguiente ejemplo:
z = 1 + 2j
None
Existe un tipo especial NoneType, que solamente puede tener un valor: None. Se usa en muchas
situaciones, pero quizás lo más común es usarla como valor devuelto por defecto de una función.
Por ejemplo, print() no devuelve nada, pero se puede capturar su valor, que es None:
return_value = print('abc')
print(return_value) # se muestra None
27
Programación en Python: Estadística a través de problemas resueltos
Expresiones y operadores
Una expresión es una combinación de constantes, variables, símbolos de operación y paréntesis
que da como resultado un valor de un tipo de dato determinado.
Una expresión combina operandos (como los literales enteros 2 o 5) y operadores (como + para
la suma) siguiendo unas reglas sintácticas similares a las de la aritmética clásica. Además, pueden
contener signos de puntuación, variables y palabras clave del lenguaje, entendidos como los valores
generados por la ejecución de esa funcionalidad (como print que devuelve None).
Un operador es un símbolo que determina la operación a realizar sobre los operandos (datos) a
los que afecta. Una operación es el conjunto de un operador y sus operandos (que pueden ser el
resultado de otra operación) y que genera un resultado único. Los operandos válidos dependen
de los tipos de datos que pueden manejar los operadores. Una operación/expresión genera un
resultado de un tipo concreto. Si un operador tiene un solo operando es un operador unario
operador operando; y si tiene dos, binario ‘operando1 operador operando2.
Operador Operación
+ Suma
- Resta
* Multiplicación
/ División
% Resto de la división
// División entera
** Potenciación
Existe una forma de realizar la división sin abandonar los enteros. El operador de división entera
en Python se representa por //, en otros lenguajes puede ser distinto. También existe el operador
% que devuelve el resto de la división entera, también llamado módulo.
28
Programación en Python: Estadística a través de problemas resueltos
x = 3
print("- Valor de x:", x) # Imprimir un valor
print("- x+1:", x + 1) # Suma: imprime "4"
print("- x-1:", x - 1) # Resta; imprime "2"
print("- x*2:", x * 2) # Multiplicación; imprime "6"
print("- xˆ2:", x ** 2) # Potenciación; imprime "9"
print("- x // 2:", x // 2) # División entera (cociente): imprime "1"
print("- x % 2:", x % 2) # Módulo (resto de la división)
x += 1 # Modificación de x
print(x) # Imprime "4"
x *= 2
print(x) # Imprime "8"
print("- Varias operaciones en una línea:", 1, 2, x, 5*2)
La tabla que muestra el resultado de las operaciones booleanas partiendo de dos valores lógicos p
y q es (true y false en minúsculas no son correctos en Python):
p q p and q p or q not p
29
Programación en Python: Estadística a través de problemas resueltos
# Operaciones Booleanas
a = 15
b = False
c = 4 + True # 5
print(c)
b = (a < 20 and a >= 0) # b es True si a esta en el intervalo [0,20)
print(b)
O por ejemplo:
v1 = True
v2 = False
print("- Valores de v1 y v2:", v1, v2)
print("- Tipo de v1:", type(v1)) # Imprime la clase de un booleano ('bool')
Los booleanos se pueden definir con el constructor de objetos bool(). Los valores de cualquier
otro tipo pueden convertirse en booleanos mediante reglas predecibles. Los números son False
si son 0 y True en cualquier otro caso. Las cadenas de texto son False si están vacías (““) y
True si contienen al menos un carácter. Las listas, tuplas, conjuntos y diccionarios, son False si
están vacíos y True si contienen elementos. El valor especial None siempre se considera False.
# Conversión a booleano
print(bool(2014)) # True
print(bool(0)) # False
print(bool(-3.5)) # True
print(bool("Hola")) # True
print(bool("")) # False
print(bool(None)) # False
Una expresión booleana recibe también el nombre de condición, su resultado será True o False
respondiendo a la situación establecida por sus operandos y operadores. Por ejemplo, si se desea
saber si un alumno ha superado cierta asignatura sabiendo que para aprobar hay que obtener un 5
o más en la calificación, se usa la expresión: Nota>=5. De esta forma, esa expresión define la
condición que debe cumplir un alumno para aprobar.
Reglas de precedencia
Cuando se combinan diferentes operadores, sobre todo los aritméticos, el valor determinado por la
expresión se calcula en función de unas reglas de precedencia que indican qué operación se ejecuta
antes. Estas reglas coinciden con lo esperable a partir de nuestros conocimientos aritméticos,
es decir, la multiplicación y la división son prioritarios con respecto a la suma y la resta. La
30
Programación en Python: Estadística a través de problemas resueltos
precedencia habitual se puede alterar utilizando paréntesis. Los paréntesis pueden anidarse unos
dentro de otros:
(3 + 2) * 5 --- su resultado es --- 25
En presencia de operadores de igual precedencia, el cálculo del valor de una expresión se realiza
típicamente de izquierda a derecha. En general, para evitar dudas y para mejorar la legibilidad, se
deben usar los paréntesis.
A continuación se muestran los operadores de más prioritarios (en la parte superior de la tabla) a
menos prioritarios (en la parte inferior):
Operador Descripción
** Exponenciación
+x, -x Especificación de signo,
*, /, //, % Multiplicación, división, división entera, resto
+, - Adición y sustracción
<, <=, >, >=, ==, != operadores relacionales
not “no” lógico
and “y” lógico
or “o” lógico
Precisión en reales
Las operaciones sobre los reales son las operaciones numéricas básicas salvo la división entera y
el resto o módulo de la división que no tienen sentido con reales. Una cosa que hay que tener
en cuenta con la aritmética de coma flotante es que su precisión es limitada, lo que puede hacer
que las pruebas de igualdad sean inestables. Esto se debe a la forma en que las computadoras
almacenan los números de punto flotante. Por ejemplo, la siguiente expresión es falsa:
0.1 + 0.2 == 0.3
31
Programación en Python: Estadística a través de problemas resueltos
Esta es la primera aparición de la notación punto [Link] con esto se indica que quiere ejecutar
la funcionalidad SS con la información contenida en XXX, siendo XXX un objeto Python. Hay que
recordar que todo son objetos en este lenguaje. Un complejo es un objeto con dos atributos, la
parte real y la parte imaginaria.
Una de las grandes aportaciones de Python es precisamente la incorporación de las listas, tuplas,
diccionarios y conjuntos como tipos de datos propios del lenguaje.
De forma general las listas, las tuplas y las cadenas (str) son parte del conjunto de las secuencias
Python que se caracterizan por estar ordenadas. Es decir, cada elemento individual que la compone,
tiene asociada una posición (i) en la secuencia a partir de 0. Toda secuencia cuenta con las
siguientes operaciones:
Operación Resultado
x in s Indica si el dato de la variable x se encuentra en s
s + t Concatena las secuencias s y t
s * n Con n un entero, concatena n copias de s
s[i] Elemento i de s, empezando por 0
s[i:j] Porción de la secuencia s desde i hasta j (no inclusive)
s[i:j:k] Porción de la secuencia s desde i hasta j (no inclusive), con paso k
len(s) Cantidad de elementos de la secuencia s
min(s) Mínimo elemento de la secuencia s
max(s) Máximo elemento de la secuencia s
32
Programación en Python: Estadística a través de problemas resueltos
El uso de [] para acceder a los elementos de las colecciones ordenadas se llaman indexación y
segmentación. Su comportamiento se mostrará para el tipo de datos listas.
Cadenas de caracteres
Una cadena de caracteres, tipo de dato str (del ingles string), representa precisamente eso, una
ristra de caracteres alfanuméricos que puede ser tratada como una unidad. Es realmente un tipo
compuesto Python. Las cadenas en Python se crean con comillas simples o dobles:
Son muchas las operaciones para trabajar con cadenas (numerosas bajo la notación punto).
Algunas están disponibles directamente sobre la base del lenguaje Python y otras muchas, están
en librerías que se pueden importar, bien sean librerías estándar o bien de terceros (éstas requieren
de una instalación adicional). Una librería no es más que un script Python con diversas funciones
o bloques de código con nombre agrupadas según algún criterio de uso, también se le llama
paquetes, bibliotecas o módulos.
Una propiedad fundamental de las cadenas es que son inmutables. Si se intenta reasignar un
carácter a una posición dentro de la cadena, no es posible:
palabra = "Python"
palabra[0] = "N" # Esto da error
Con str() se crea y/o se convierte cualquier valor a cadena. El resultado es una cadena con el
mismo contenido que hubiera mostrado el comando print() sobre la expresión entre paréntesis:
x = 76
b = str(x)
print(x," ", b) # el resulado es: 76 76
f-Strings
Un f-String es una cadena que está asociada a una o varias variables, permitiendo generar una
cadena formateada. Si se utiliza f"XX {var} XXX" se está indicando que esa cadena debe
construirse poniendo el contenido de la variable var en donde aparece en la cadena.
33
Programación en Python: Estadística a través de problemas resueltos
mensaje = 'Gracias'
respuesta = 'De nada'
frase = f"Para el mensaje: {mensaje} hay una respuesta: {respuesta}."
print(frase)
producto = 'Calabacín'
cajas = 1000
precio = 1.25
# Formateo de la información
detalle_producto = f"{producto:>10s} {cajas:10d} cajas"
costo_total = f"Costo total = {cajas * precio:,.2f} Euros"
Las cadenas ofrecen una amplia variedad de métodos para validar y manipular textos. Siendo s
una cadena algunos de estos métodos se muestran en la tabla, incluyendo a continuación diversos
ejemplos de uso:
Método Descripción
[Link](suffix) Verifica si termina con el sufijo
[Link](t) Primera aparición de t en s (o -1 si no está)
[Link](t) Primera aparición de t en s (error si no está)
[Link]() Verifica si los caracteres son alfabéticos
[Link]() Verifica si los caracteres son numéricos
[Link]() Verifica si los caracteres son minúsculas
[Link]() Verifica si los caracteres son mayúsculas
[Link](slist) Une una lista de cadenas usando s como delimitador
[Link]() Convertir a minúsculas
[Link](old,new) Reemplaza texto
[Link]([delim]) Parte la cadena en subcadenas
[Link](prefix) Verifica si comienza con un prefijo
[Link]() Elimina espacios en blanco al inicio o al final
[Link]() Convierte a mayúsculas
Para dividir una cadena, generando una lista de palabra se utiliza split.
34
Programación en Python: Estadística a través de problemas resueltos
Si se desea unir las cadenas de una lista palabras en una sola cadena, separando los elementos
con un símbolo definido, en el ejemplo -.
unida = "-".join(palabras)
Tipo lista
Es un tipo compuesto que puede almacenar distintos valores (llamados ítems o componentes) y
que pueden ser modificados. Para crear una lista basta con poner sus componentes entre [ ]
(corchetes) y separarlos con , (comas).
Las listas son ordenadas, es decir, mantienen el orden en el que se definen independientemente
de los valores de sus componentes. Pueden contener elementos de tipos arbitrarios, incluso otras
listas, permitiendo la creación de estructuras anidadas. El acceso a los elementos se realiza por
posición, utilizando índices entre corchetes [i] y como son dinámicas, se pueden añadir o eliminar
elementos. Las listas son equivalentes a las colecciones indexadas y estructuradas clásicas, como
los vectores de otros lenguajes.
Las listas especialmente útiles para almacenar y manipular colecciones de datos relacionados. Por
ejemplo, se pueden utilizar para guardar los nombres de los clientes de un negocio, los precios de
los productos en una tienda o las calificaciones de los estudiantes en una clase.
Se utiliza la indexación basada en cero, por lo que se accede al primer y segundo elemento
mediante de la lista L con:
print(L[0], L[1])
35
Programación en Python: Estadística a través de problemas resueltos
Se puede acceder a los elementos contando del final al principio de la lista con números negativos,
comenzando desde -1.
L[-2] # Devuelve 7
L[ 2] # Devuelve 5
Se crea una lista n que toma los elementos desde el índice 0 hasta el índice 3 (sin incluirlo). [2,
3, 5] Pero se puede omitir el primer índice, 0, escribendo su equivalente:
Del mismo modo, si se omite el último índice, el valor predeterminado es la longitud de la lista.
Por lo tanto, se puede acceder a los últimos tres elementos de la siguiente manera:
L[-3:] # Devuelve [5, 7, 11]
Finalmente, es posible especificar un tercer entero que represente el tamaño de paso/salto. Por
ejemplo, para seleccionar cada segundo elemento de la lista, se escribe:
L[::2] # equivalente a L[0:len(L):2]
Una versión muy útil es especificar un tamaño de paso negativo, lo que invertirá la lista:
L[::-1]
36
Programación en Python: Estadística a través de problemas resueltos
Otra peculiaridad, es que se pueden combinar asignación y segmentación para modificar en bloque
varios ítems de la lista:
Asignar una lista vacía equivale a borrar los ítems de la lista o sublista:
letras[:3] = []
Las listas son un elemento muy abierto que permite mezclar elementos de distinto tipo:
Incluso listas dentro de listas (llamadas listas anidadas). Se pueden manipular fácilmente este
tipo de estructuras utilizando múltiples índices, como si fuesen las filas y columnas de una
tabla. Para representar una matriz se puede trabajar con listas de listas, es decir, listas anidadas
metro=[[1,2,3],[4,5,6],[7,8,9]] y se puede acceder al elemento usando la notación
variable[fila][columna].
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
metro = [a, b, c]
De esta forma, se puede representar información agrupada por entidades, muy similar a las clásicas
colecciones estructuradas o registros. Por ejemplo, los nombres del los agricultores, su municipio y
el total de hectareas de su explotación:
37
Programación en Python: Estadística a través de problemas resueltos
agricultores = [
["Miguel", "Almería", 1200],
["Ana", "Tahal", 1700],
["Lourdes", "Berja", 1500]
]
print(agricultores)
print("")
print(agricultores[1])
print(*agricultores[1]) # Separa por espacios datos del segundo agricultor
Método/operación Descripción
n=len(lista) Guarda en n la logitud de la lista
[Link](x) Añade el valor x a la lista
lista + [13, 17] Genera una lista que concatena lista con [13, 17]
[Link]() Deja la lista vacía
[Link](3) Eliminar elementos de lista con valor 3
[Link]() Invierte los elementos de lista
del lista[3] Borra el elemento de la posición 3 de lista
[Link](lista2) Concatena la primera lista con la segunda
[Link](6,5.5) Inserta el valor 5.5 en la posición 6 de lista
[Link]([i]) Elimina y devuelve el elemento en la posición i de lista. Si no se
especifica i, elimina y devuelve el último elemento
[Link]() Elimina todos los elementos de lista, dejándola vacía
[Link](x) Devuelve el número de veces que el valor x aparece en lista
[Link](x[, Devuelve el índice de la primera aparición de x en lista.
start[, end]]) Opcionalmente, se puede especificar un rango con start y end
sorted(lista) Devuelve una lista ordenada a partir de lista
[Link]() Ordena lista
c = [Link]() Devuelve una copia de lista
38
Programación en Python: Estadística a través de problemas resueltos
Para dejar la lista original intacta pero obtener una nueva lista ordenada a partir de ella, se usa la
función sorted.
bs = [7, 4, 6, 8, 3]
cs = sorted(bs)
print(bs)
print(cs)
Tipo tupla
Las tuplas son inmutables lo que las hace ideales para almacenar datos como coordenadas
geográficas, configuraciones del sistema o puntos en un plano cartesiano. Son una excelente
opción si se quiere que los datos de una colección sean solamente de lectura. Se crean con ()
(paréntesis):
semana = ('Lunes','Martes','Miércoles','Jueves','Viernes','Sábado','Domingo')
t = (1, 2, 3)
Un caso particularmente común de uso de las tuplas es en funciones que tienen múltiples valores
de retorno. Por ejemplo, el método as_integer_ratio() se usa con valores reales y devuelve
un numerador y un denominador cuyo cociente se aproxima a ese real. Este doble valor de retorno
viene en forma de una tupla:
x = 0.125
t = x.as_integer_ratio()
print(t)
print("numerador", t[0])
print("denominador", t[1])
39
Programación en Python: Estadística a través de problemas resueltos
x = 0.5
numerador, denominador = x.as_integer_ratio()
print(numerador, "/", denominador)
A las tuplas se le pueden aplicar algunos de los métodos de listas : list(), sorted(), o count().
Sin embargo otros no pueden ser utilizados directamente sobre las tuplas por ser inmutables:
append(), extend(), insert(), remove(), pop() o reverse(). Para ser utilizados se
debería crear una nueva tupla que contenga la copia de los elementos modificados:
tupla_original = (1, 2, 3, 4, 5)
tupla_invertida = tuple(reversed(tupla_original))
Tipo conjunto
A diferencia de las listas y las tuplas, los conjuntos no permiten elementos duplicados. Admiten
las operaciones de la teoría de conjuntos como unión, intersección y diferencia, lo que los hace
muy útiles para resolver problemas de lógica y teoría de conjuntos. Se puede verificar rápidamente
si un elemento pertenece a un conjunto, lo que los convierte en una estructura de datos eficiente
para realizar búsquedas.
Los conjuntos son útiles cuando se necesita trabajar con datos únicos, como palabras en un texto,
usuarios en una red social o elementos de una colección. También pueden ser valiosos para realizar
operaciones de filtrado y clasificación de datos. Se definen utilizando llaves, { } :
primos = {2, 3, 5, 7}
impares = {1, 3, 5, 7, 9}
Cada operación tiene dos métodos equivalentes, uno usando un operador y otro con un método:
# union:
primos | impares # con operador
[Link](impares) # con método equivalente
# intersección
primos & impares # con operador
[Link](impares) # con método equivalente
# diferencia
primos - impares # con operador
[Link](impares) # con método equivalente
40
Programación en Python: Estadística a través de problemas resueltos
Otra forma de crear conjuntos es utilizando la función set(), a la que se le pasa como argumento
alguna otra colección (cadenas, listas, tuplas, conjuntos y diccionarios).
Una forma simple de quitar los duplicados de una lista aprovechando las propiedades de los
conjuntos:
Están disponibles muchos más métodos y operaciones de conjuntos que se pueden consultar en la
documentación de Python. Algunos ejemplos son:
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
s3 = {1, 2, 3}
s4 = {1, 2, 3, 4, 5}
s5 = {1, 2, 3}
s6 = {1, 2, 3}
# Saber si es subconjunto
print(s1 <= s2)
print([Link](s2))
Tipo diccionario
Los diccionarios son colecciones no secuenciales y mutables de elementos. Están compuestos
por una clave (que identifica de modo único al elemento) y el valor que se desea almacenar.
Esto permite acceder rápidamente a los datos mediante una clave única en lugar de una posición
numérica. Los diccionarios son ideales para representar colecciones estructuradas (registros), como
la información de usuarios o registros en bases de datos. Por ejemplo, se puede usar un diccionario
para almacenar información sobre una persona, con claves como “nombre”, “edad” y “ciudad”.
Gracias a su rapidez en la búsqueda y actualización de valores, los diccionarios son una opción
potente para trabajar con grandes volúmenes de datos. Se pueden crear a través de una lista
separada por comas de pares key:value dentro de llaves. No pueden aparecer elementos con la
misma clave y son muy versatiles:
41
Programación en Python: Estadística a través de problemas resueltos
asig = {
'asignatura': 'Fundamentos de Programación', 'créditos': 6,
'tipos': ['teoría', 'prácticas']
}
nom = {
'persona': {
'nombre': 'Eva','primer apellido':'García', 'segundo apellido':'Pérez'
},
'edad': 30
}
print(num)
print(asig)
print(nom)
Se accede a los elementos y se modifican a través de la sintaxis de indexación utilizada para listas
y tuplas, excepto que aquí el índice no es un orden basado en cero, sino una clave válida en el
diccionario:
# Acceso via la clave
numeros['uno']
Los diccionarios no mantienen ningún sentido de orden para los parámetros de entrada. Esta falta
de ordenación permite que se implementen de manera eficiente, así el acceso rápido a elementos
aleatorios, independientemente del tamaño del diccionario.
Por ejemplo, si se quiere guardar una base de datos de personas con el nombre, edad, teléfono y
dirección de una persona se puede utilizar un diccionario:
nombre = input('¿Cómo te llamas? ')
edad = input('¿Cuántos años tienes? ')
direccion = input('¿Cuál es tu dirección? ')
telefono = input('¿Cuál es tu número de teléfono? ')
Otro caso de uso de un diccionario es almacenar tres productos fitosanitarios. Cada producto
está representado por otro diccionario que contiene diferentes campos de información, como el
nombre del producto, los ingredientes activos, el fabricante, el precio y la cantidad disponible.
De esta forma, si se definen tres productos fitosanitarios insecticida1, fungicida1 y herbicida1, el
diccionario en el que se guarda su información sería:
42
Programación en Python: Estadística a través de problemas resueltos
Para acceder a un elemento específico del diccionario, se utiliza la clave correspondiente. Por
ejemplo, para acceder al nombre del primer producto, se utiliza la siguiente sintaxis:
nombre_producto1 = productos_fitosanitarios["insecticida1"]["nombre"]
print(nombre_producto1) # Imprime "Insecticida A"
A modo de resumen
La elección del tipo de colección a utilizar depende en gran medida de la naturaleza del problema
que se esté abordando y de las características específicas de los datos con los que se va a trabajar.
Si se requiere almacenar información que pueda cambiar con el tiempo, agregando o eliminando
elementos de manera dinámica, las listas son una excelente opción, ya que permiten modificaciones
flexibles y un fácil recorrido secuencial.
Por otro lado, cuando los datos deben permanecer inmutables o se necesita asegurar que su
posición se conserve, las tuplas son la opción más adecuada. Son especialmente útiles para
almacenar pares de valores que deben mantenerse juntos sin cambios.
Si el objetivo es eliminar duplicados o realizar operaciones de conjuntos, como intersecciones
o uniones, los conjuntos son ideales, ya que almacenan elementos únicos y permiten verificar
43
Programación en Python: Estadística a través de problemas resueltos
Instrucción de asignación
En programación, la asignación es la acción que consiste en dar o asignar el valor de una expresión
a una variable, permitiendo almacenar datos en memoria para su uso posterior. Esta instrucción
se ejecuta mediante el operador de asignación, que en muchos lenguajes de programación es el
símbolo igual (=):
variable = expresión
44
Programación en Python: Estadística a través de problemas resueltos
x = 4
y = 5
z = (2 * x + y) % 3
Una asignación es destructiva puesto que se pierde el valor anterior de la variable. Es distinta
de la igualdad matemática. El tipo de dato del resultado de la expresión ha de coincidir con el
de la variable en la que se almacena. En muchos lenguajes de programación se relaja esta regla,
controlando el traductor algunas conversiones de tipos de datos, por ejemplo almacenar un valor
entero en una variable real.
Se pueden utilizar expresiones con operadores que admiten varios tipos de datos (normalmente
numéricos). En estas situaciones se hace la conversión del resultado y de los cálculos intermedios
al tipo de dato con rango más amplio (en cuanto al espacio en memoria). :
x = 4
y = 5.5
z = (2 * x + y) % 3
En lenguajes como el C las variables son como contenedores o cajones donde se ponen datos:
// C code
int x = 4;
Se está creado una zona de memoria según tan grande como el tamaño de los enteros (2 bytes)
llamada x, y se pone el valor 4 en ella. Por contra Python considera las variables como referencias
o punteros. Si en Python se escribe x = 4 esencialmente se define un puntero llamado x que
guarda la dirección de memoria donde se ha puesto el valor 4.
x = 4.0 # s es un real
x = 1 # x es un entero
x = 'hello' # ahora x una cadena
x = [1, 2, 3] # ahora x es una lista
Hay que tener cuidado porque una consecuencia del uso de las variables como puntero/referencia
para tipos compuestos, es que si se trabaja con dos nombres de variable sobre el mismo objeto,
cuando se cambia sobre una variable la otra también se ve afectada. Por suerte esto no pasa con
los tipos simples. Esta situación también aparece en otros lenguajes como JAVA. Por ejemplo:
45
Programación en Python: Estadística a través de problemas resueltos
Se crean dos variables x e y que apuntan al mismo objeto. Al añadir elementos a x afecta también
a y.
No obstante para números, cadenas y otros tipos simples, es perfectamente seguro hacer opera-
ciones como las siguientes:
x = 10
y = x
x = x + 5 # sumamos 5 al valor de x, y se le asigna a x
print("x =", x) # x = 15
print("y =", y) # y = 10
Cualquier operador matemático puede ser utilizado antes de = para hacer una operación embe-
bida, simplificando así la escritura:
Asignacion
-= disminuir la variable
+= incrementar la variable
*= multiplicar la variable
/= divide la variable
//= división entera de la variable
%= devuelve sobre la variable su módulo
**= elevar a una potencia
a += 1 # a = a + 1
print (a)
a *= 2 # a = a * 2
print (a)
46
Programación en Python: Estadística a través de problemas resueltos
La función input() permite obtener texto escrito por teclado sobre variables. Al llegar a la
función, el programa se detiene esperando que se escriba algo y se pulse la tecla < intro >:
Su conjunción con los f-string y print permite generar la interacción con el usuario en la que
se mezclan diferentes tipos de variables. Puesto que lo que se leen son cadenas, si se quieren leer
números se tiene que hacer la conversión, muchas vesces sobre la misma línea:
eur_a_usd = 1.10
actual = 2025
print(f"\nHola, {nombre}.")
print(f"Tu edad es {edad} años, naciste aproximadamente en {actual - edad}.")
print(f"{euros:.2f} C son {euros * eur_a_usd:.4f} dólares.")
También se pueden hacer lecturas múltiples con comprensión de listas (que se verán con detalle
más adelante, puesto que suponen una instrucción de iteración for),
Ya se han mostrado divesos ejemplos del uso de otra principal instrucción de salida, print, que
se puede utilizar tanto en datos simples como en datos compuestos:
Se puede usar expresiones sobre la salida. Se aplica el operador y después se imprime el resultado:
print ("Bienvenidos")
print (8 * "\n")
print ("Bienhallados")
47
Programación en Python: Estadística a través de problemas resueltos
De forma predeterminada, la función de impresión en Python termina con una nueva línea si no
se cambia con el parámetro end. En este caso, se usa un espacio mostrándose los dos mensajes
en la misma línea y acabando con !.
print ("Bienvenidos", end = ' ')
print ("al maravilloso mundo de la PROGRAMACION", end = '!')
Para personalizar los mensajes se pueden usar las variables con distintas estrategias:
nombre = "Juan"
edad = 25
# Usando f-strings
print(f"Hola, mi nombre es {nombre} y tengo {edad} años.")
# Al estilo de C
print("Hola, me llamo %s y tengo %s años." %(nombre, edad))
# Usando índices
print("Hola, me llamo {0} y tengo {1} años.".format(nombre, edad))
# Usando nombres
print("Hola, me llamo {nom} y tengo {ed} años.".format(nom=nombre, ed=edad))
Para la entrada de datos es habitual la combinación de input y split, que permite separar en
datos individuales una cadena leída:
entrada = input("Introduce tres números separados por espacios: ")
numeros = [Link]() # Se genera una lista con los datos
print("Día:", fecha[0])
print("Mes:", fecha[1])
print("Año:", fecha[2])
48
Programación en Python: Estadística a través de problemas resueltos
num = 10
div = 0 # Esto provocará una excepción de división por cero
try:
res = num / div # Aquí ocurre la excepción
print(res) # no se ejecutará si hay un error
except ZeroDivisionError: # Captura la excep. concreta de división por cero
print("Error: Intentaste dividir entre cero :( ")
Existen diferentes tipos de excepciones según el error que se produzca. Las que se generan
por conversión se dice que son de tipo ValueError y las de división por cero son del tipo
ZeroDivisionError.
try:
numero = int(input("Introduce un número entero: "))
print (numero)
# procesar el número leido
#....
except ValueError:
print("Error: Debes ingresar solo números enteros, no letras \
ni caracteres especiales.")
49
Programación en Python: Estadística a través de problemas resueltos
input(), list(), dict(), min(), max(), sum(), sorted(), open(), file(), help(), y
dir().
# Impresión
print ("hola")
# Máximo
max(22,44)
# Conversión a entero
numero = int("20")
print(numero) # Salida: 20
Existen otras funciones que para poder ser utilizadas requiren de la importación del paquete o
librería donde estan definidas. Por ejemplo:
Estas funciones no son parte del lenguaje base, sino del paquete math, os, o shutil, por lo que
para utilizarlas, el programa deberá comenzar con la línea import math, como en el siguiente
ejemplo:
50
Programación en Python: Estadística a través de problemas resueltos
De todas las librerías que no requiren instalación adicional en Python, las llamadas librerías
estándar, las más habituales son:
Librería Descripción
collections Estructuras de datos adicionales
csv Procesamiento de los archivo separados por comas
datetime Gestión de fechas y tiempos
decimal Aritmética de punto fijo y de punto flotante, incluidos cálculos monetarios
math Operaciones y constantes matemáticas habituales
os Interacciones con el sistema operativo
random Números pseudoaleatorios
re Gestión de expresiones regulares
statistics Funciones de estadística matemática como la media, la mediana, la moda y la
varianza.
sys Procesamiento de argumentos de línea de comandos; flujos de entrada
estándar, salida estándar y error estándar
timeit Análisis de rendimiento
functools Herramientas para programación funcional, como lru_cache y partial
itertools Generadores y combinaciones eficientes para trabajar con iteradores
json Manejo de datos en formato JSON
pathlib Manejo más moderno de rutas de archivos en comparación con [Link]
subprocess Ejecutar comandos del sistema operativo desde Python
uuid Generación de identificadores únicos universales (UUID)
51
Diseño y Control de Flujo en
Programación
52
Programación en Python: Estadística a través de problemas resueltos
La prueba o validación consiste en la ejecución del código para comprobar que las salidas generadas
se corresponden con la solución esperada. Estos casos de prueba se construyen durante el análisis
y representan las situaciones a las que se puede dar solución.
Estos pasos se muestran a continuación para un Problema concreto: Construir un programa que
calcule e imprima en pantalla la velocidad de un cuerpo en movimiento en metros por segundo.
Análisis: Se debe decidir si se trata de un movimiento circular en cuyo caso hay que conocer la
trayectoria o la velocidad angular o un movimiento rectilíneo uniforme con un movimiento entre
dos puntos A y B, que será que se abordará en este caso. La información de la que se dispone
es la velocidad inicial del cuerpo (v0 ), la distancia entre A y B (dist) y el tiempo empleado (t).
Según las leyes de la cinemática las posibles fórmulas a aplicar son: v = v0 + at o bien v = dist/t
para calcular la velocidad media.
Diseño: Para este problema, el diseño global consiste en decidir cual de las dos fórmulas se va a
utilizar. En este caso puesto que no se tiene el valor de la aceleración se calculará la velocidad
media usando el cociente entre la distancia y el tiempo.
en lenguaje natural
53
Programación en Python: Estadística a través de problemas resueltos
en Pseudocódigo
El pseudocódigo es un lenguaje artificial que ayuda a desarrollar algoritmos antes de convertirlos
en programas. Se centra en la lógica del problema más que en los detalles sintácticos.
Algoritmo calcularVelocidad
Escribir "Introduce la distancia recorrida (m): ";
Leer distancia
Escribir "Introduce el tiempo empleado (s): ";
Leer tiempo;
velocidad <- distancia / tiempo;
Escribir "La velocidad es: ", velocidad, " m/s";
FinAlgoritmo
try:
velocidad = distancia / tiempo # Si tiempo es 0, ZeroDivisionError
print(f"\nLa velocidad es: {velocidad:.2f} m/s")
except ZeroDivisionError:
print("\nError: El tiempo no puede ser 0.")
finally:
print("\nCálculo finalizado.")
En este código hay algunas de las características sintácticas de Python que conviene revisar.
Los espacios en blanco dentro de las líneas no son importantes. Mientras que el mantra
del espacio en blanco importa es válido al principio de las líneas, porque indican un bloque de
código, el espacio en blanco dentro de las líneas de código Python no importa.
Por ejemplo, estas tres expresiones son equivalentes:
radio = diametro/2
radio = diametro / 2
radio = diametro / 2
54
Programación en Python: Estadística a través de problemas resueltos
El uso eficaz de los espacios en blanco puede dar lugar a un código mucho más legible. A la vista
está, en este código donde se calcula la potencia con exponente de un número negativo:
Poner espacio a ambos lados de un operador binario simplifica la lectura. De igual forma, conviene
separar las partes de los script mediante uso de líneas en blanco, como la separación del mensaje
de salida, la lectura de datos de entrada y el cálculo, como en el script anterior.
Amigabilidad de los script. Es conveniente utilizar mensajes que faciliten la interacción con el
usuario. Por ejemplo, los dos primeros printno son necesarios pero facilitan la ejecución. Además,
es conveniente utilizar mensajes cuando se piden datos al usuario, el código funcionaría sin ellos,
pero no es recomendable.
distancia = float(input())
tiempo = float(input())
def velocidad():
print("CÁLCULO DE VELOCIDAD\n")
print("=====================\n")
try:
velocidad = distancia / tiempo # Si tiempo es 0, ZeroDivisionError
print(f"\nLa velocidad es: {velocidad:.2f} m/s")
except ZeroDivisionError:
print("\nError: El tiempo no puede ser 0.")
finally:
print("\nCálculo finalizado.")
import velocidad
[Link]()
55
Programación en Python: Estadística a través de problemas resueltos
Instrucción secuencial
Los bloques de código se ejecutan uno tras otro y las instrucciones dentro de cada bloque también,
siguiendo el orden físico de escritura. En Python el fin de cada instrucción es el fin de línea. La
salida de cada instrucción o cada bloque es la entrada de la siguiente. Un bloque de código es
considerado como una única instrucción secuencial respecto al resto de bloques o instrucciones de
su mismo nivel.
56
Programación en Python: Estadística a través de problemas resueltos
n50 = litros // 50
litros = litros % 50
n20 = litros // 20
litros = litros % 20
n10 = litros // 10
litros = litros % 10
n5 = litros // 5
litros = litros % 5
n2 = litros // 2
n1 = litros % 2
57
Programación en Python: Estadística a través de problemas resueltos
sx = v1x + v2x
sy = v1y + v2y
sz = v1z + v2z
58
Programación en Python: Estadística a través de problemas resueltos
Selectiva simple
Se ejecuta una determinada acción cuando se cumple la condición especificada. Su funcionamiento
comienza con la evaluación una condición, si ésta es verdadera entonces ejecuta un bloque de
instrucciones; y si es falsa, no se ejecuta nada.
Por ejemplo:
Selectiva doble
Es la estructura que permite elegir entre dos opciones o alternativas posibles, en función del
cumplimiento o no de una determinada condición. Primero se evalúa la condición y si ésta es
verdadera, se ejecuta un bloque de instrucciones especificado justo después de la condición
(habitualmente llamada parte ìf); y en caso contrario, es decir cuando la condición es falsa, se
ejecuta otro bloque diferente de instrucciones, especificado después de la palabra else.
Por ejemplo:
O bien:
59
Programación en Python: Estadística a través de problemas resueltos
Selectivas anidadas
El funcionamiento se basa en evaluar la primera condición y, si se cumple, ejecutar su bloque de
código donde puede haber otra estructura condicional que refine aún más la decisión. Permite
manejar escenarios más complejos. Hay que cuidar la claridad del código para evitar confusión y
saber exactamente qué bloque incluye, qué está dentro de cada parte. Por ejemplo:
if (password == 'miClaveSegura'):
print("Además es la contraseña correcta.")
else:
print("Pero es incorrecta.")
else:
print('Tu contraseña es muy corta e insegura.')
if (password != 'miClaveSegura'):
print("Además, es incorrecta (por supuesto).")
x = -15
if x == 0:
print(x, "es cero")
elif x > 0:
print(x, "es positivo")
elif x < 0:
print(x, "es negativo")
else:
print(x, "es difícil que lo hayas visto alguna vez...")
Selectiva múltiple
Esta estructura, no presente en Python, evalúa una expresión que puede tomar n valores distintos
(1, 2, 3,. . . , N) y según este valor el algoritmo seguirá un determinado camino entre n posibles.
En C o Java se corresponde con la instrucción switch. En Python se pueden utilizar una cadena
de if para recoger este comportamiento:
60
Programación en Python: Estadística a través de problemas resueltos
if dia == 1:
print('lunes')
if dia == 2:
print('martes')
if dia == 3:
print('miércoles')
if dia == 4:
print('jueves')
if dia == 5:
print('viernes')
if dia == 6:
print('sábado')
if dia == 7:
print('domingo')
if dia < 1 or dia > 7:
print('error')
Implementación alternativa,
LIMITE = 120 # Al ser constante por legibilidad se usan mayúsculas
try:
v = float(input("la velocidad actual es: "))
except ValueError:
print("El valor ingresado no es un numero válido.")
else:
if (v > LIMITE):
exceso = v - LIMITE
print("Sobrepasado limite de velocidad en", exceso," Km/h")
61
Programación en Python: Estadística a través de problemas resueltos
ValueError es una excepción generara automáticamente por el lenguaje para recoger que se ha
producido un error en la trasformación de cadena a entero, por ejemplo si se escriben letras en
lugar de números.
Validación de datos simple. Para evitar errores y mejorar la robustez del programa se desea
que el código valide los datos de entrada, es decir, no se genere error y funcione correctamente
cuando el usuario introduce una entrada incorrecta. Por ejemplo, si se da un número negativo
cuando se espera un número positivo, o bien una entrada errónea escribiendo letras cuando se
pide un número. Para gestionar posibles errores se deben incluir comprobaciones adicionales o
incluso bloques try/except.
Dado el código que lee un entero positivo:
try:
entero=int(input('Introduzca un entero positivo: '))
if entero >=0:
## procesar entero
print ("procesamos entero")
else:
print("dato no valido")
except ValueError: # generada si la trasformación es errónea
print("dato erróneo")
62
Programación en Python: Estadística a través de problemas resueltos
En este ejemplo, se podría incluir la validación de la temperatura en dos sentidos: que sea un
número sin caracteres alfanuméricos controlando la conversión (ValueError) o también que sea
una temperatura válida mayor que el mínimo absoluto (-273º). Pero si se informa al usuario que
este script solo funciona correctamente con números mayores de -272 no sería necesario incluir la
validación, es responsabilidad del usuario dar los datos correctos.
Esta versión es mucho más compleja y en cierto sentido oculta la funcionalidad del script, tal
como se ha indicado, se debe llegar a un compromiso que mejore la legibilidad y mantenibilidad.
Ejercicio resuelto: ¿Cuál es la nota?
Dada una nota numérica mayor que 0 de un alumno/a indicar su calificación según la siguiente
tabla:
63
Programación en Python: Estadística a través de problemas resueltos
if nota < 0:
print("La nota no puede ser negativa.")
elif nota < 5:
print("Insuficiente")
elif nota < 7:
print("Aprobado")
elif nota < 9:
print("Notable")
elif nota < 10:
print("Sobresaliente")
elif nota == 10:
print("Matrícula de honor")
else:
print("La nota ingresada no es válida.")
Construir un programa que calcule e imprima en pantalla las raíces de la ecuación de segundo
grado: Ax2 + Bx + C = 0, dados por teclado los coeficientes A, B y C.
64
Programación en Python: Estadística a través de problemas resueltos
La estrategia de solución es resolver el problema controlando las condiciones basadas en los valores
de los coeficientes, siguiendo estos pasos:
Se leen los coeficientes.
Se estudia el valor de A para saber si se trata de en una ecuación de segundo grado.
Si es de segundo grado se calcula el discriminante.
Según el valor del discriminante se define el resultado.
Si no es una ecuación de segundo grado — No esta especificado pero se puede optar o no
por dar una solución, si no es de segundo grado no entra dentro del objetivo del programa
A B C Resultado
<>0 - - Estudiar discriminante
0 - - No de segundo grado
0 0 0 No ecuación
0 0 <>0 Imposible
Implementación
if a == 0:
if b == 0:
if c == 0:
print("0=0")
65
Programación en Python: Estadística a través de problemas resueltos
else:
print("Imposible")
else:
x = -c / b
print("una única solución ", x)
else:
discriminante = b * b - 4 * a * c
if discriminante < 0:
print(f"La ecuación no tiene soluciones reales.")
x1 = (-b + complex(discriminante) ** (1 / 2)) / (2 * a)
x2 = (-b - complex(discriminante) ** (1 / 2)) / (2 * a)
print("La ecuación tiene dos soluciones complejas conjugadas:")
print("x1 =", x1)
print("x2 =", x2)
else:
raiz = sqrt(discriminante)
x_1 = (-b + raiz) / (2 * a)
if discriminante != 0: # se comprueba si hay otra solución
x_2 = (-b - raiz) / (2 * a)
print(f"Las soluciones son {x_1} y {x_2}.")
else:
print(f"La única solución es x = {x_1}")
Pero ¿es necesario siempre contemplar todas las situaciones? En este caso contemplar a=0 o
b=0 o c=0. Depende de la especificación (análisis) del problema, que define el “contrato” de
uso del código. Si a quién use este código se le informa de que solo funciona correctamente con
ecuaciones de segundo grado completas el resultado podría simplificarse:
discriminante = (b**2) - (4 * a * c)
if discriminante > 0:
x1 = (-b + sqrt(discriminante)) / (2 * a)
x2 = (-b - sqrt(discriminante)) / (2 * a)
print("La ecuación tiene dos soluciones reales distintas:")
print("real x1 =", x1)
print("real x2 =", x2)
elif discriminante == 0:
x = -b / (2 * a)
print("La ecuación tiene una solución real doble:")
print("x =", x)
else:
x1 = (-b + complex(discriminante)**(1/2)) / (2*a)
x2 = (-b - complex(discriminante)**(1/2)) / (2*a)
print("La ecuación tiene dos soluciones complejas conjugadas:")
print("x1 =", x1)
print("x2 =", x2)
Esta misma situación se plantea para la validación de los datos de entrada, por ejemplo, si se
quiere incluir la validación de que si se introducen caracteres alfanuméricos no se ejecuten los
66
Programación en Python: Estadística a través de problemas resueltos
cálculos :
try:
a = float(input('Dame el coeficiente a: '))
b = float(input('Dame el coeficiente b: '))
c = float(input('Dame el coeficiente c: '))
except ValueError:
print("Error: Los coeficientes deben ser valores numéricos.")
else:
if a == 0:
if b == 0:
if c == 0:
print("0=0")
else:
print("Imposible")
else:
x = -c / b
print("una única solución ", x)
else:
discriminante = b * b - 4 * a * c
if discriminante < 0:
print(f'La ecuación no tiene soluciones reales.')
x1 = (-b + complex(discriminante) ** (1 / 2)) / (2 * a)
x2 = (-b - complex(discriminante) ** (1 / 2)) / (2 * a)
print("La ecuación tiene dos soluciones complejas conjugadas:")
print("x1 =", x1)
print("x2 =", x2)
else:
raiz = sqrt(discriminante)
x_1 = (-b + raiz) / (2 * a)
if discriminante != 0:
x_2 = (-b - raiz) / (2 * a)
print(f'Las soluciones son {x_1} y {x_2}.')
else:
print(f'La única solución es x = {x_1}')
67
Programación en Python: Estadística a través de problemas resueltos
Implementación
entrada = input("Dime tres temperaturas separadas por espacios (t1 t2 t3): ")
temperaturas = [Link]()
t1 = float(temperaturas[0])
t2 = float(temperaturas[1])
t3 = float(temperaturas[2])
print(tmax)
Como mejora y como ejemplo de variable indicador si se incluye una variable se puede marcar
cual es la mayor de las temperaturas en términos del orden de lectura.
68
Programación en Python: Estadística a través de problemas resueltos
Finalmente, se incluye una solución muy al estilo Python, que aplica el principio de la reutilización
de código. Se usa una lista aun a sabiendas de que se desperdicia espacio, puesto que no es
necesario utilizarla. Se genera un código más corto pero más ineficiente, donde los condicionales
están ocultos en la llamada a max().
entrada = input("Dime tres temperaturas separadas por espacios (t1 t2 t3): ")
temperaturas = list(map(float, [Link](" ")))
tmax = max(temperaturas)
print("La mayor temperatura introducida es:", tmax)
FIJO = 30
T1 = 15
T2 = 10
69
Programación en Python: Estadística a través de problemas resueltos
print("Hola Mundo")
print("Hola Mundo")
print("Hola Mundo")
print("Hola Mundo")
print("Hola Mundo")
o bien podría hacerse aprovechando las propiedades que tiene la función print:
print(5*"Hola Mundo\n")
Pero esto sólo pasa con print, si cada iteración es un código complejo o bien supone mas de
una instrucción, al solución utilizar alguna construcción que indique cuántas veces se ha de hacer:
Elementos de un bucle
Un bucle tiene dos elementos básicos: condición y cuerpo del bucle. La condición que es la
expresión lógica que activa la repetición del bucle o la salida de este, por ejemplo, si se han
ejecutado un cierto número de veces el cuerpo del bucle. El cuerpo del bucle son el conjunto de
instrucciones que se ejecutan de forma repetitiva, cada ejecución del cuerpo del bucle se llamará
70
Programación en Python: Estadística a través de problemas resueltos
Python incluye únicamente dos tipos de bucle: while y for de definen un bloque de código desde
el inicio de los dos puntos (:).
while condicion:
cuerpo_del_bucle
Bucle while
La ejecución de una estructura de control while comienza evaluando la condición que sigue a
la palabra clave. Si el resultado es True, se ejecuta el cuerpo del bucle, y una vez ejecutado, el
proceso se repite volviendo a evaluar la condición y, si sigue siendo cierta, el cuerpo del bucle se
ejecuta otra vez. Este ciclo continúa mientras la condición se mantenga verdadera. Si el resultado
de una evaluación es False, el cuerpo del bucle no se ejecuta y se pasa a la siguiente parte del
programa, es decir, la siguiente instrucción o bloque tras el bloque while.
La variable o las variables que aparezcan en la condición se suelen llamar variables de control.
Las variables de control deben definirse antes del bucle while y modificarse en el cuerpo el bucle.
En el bucle while el número de iteraciones no está definida antes de empezar el bucle, por ejemplo
porque depende de los datos los proporciona el usuario o de los cambios realizados en el cuerpo
del bucle.
71
Programación en Python: Estadística a través de problemas resueltos
print(numero**0.5)
O bien, para calcular la temperatura máxima registrada hasta que el usuario introduce un valor
por debajo del cero absoluto (-273.15°C), y siempre que se den valores válidos de temperaturas.
TLIM = -999 # Valor centinela
temperatura = 0 # Valor basura, nunca se usará
temperatura_maxima = TLIM # Inciialización con un valor no válido
repetir = True
while repetir:
distancia = float(input("Introduce la distancia recorrida (m): "))
tiempo = float(input("Introduce el tiempo empleado (s): "))
try:
velocidad = distancia / tiempo # lanza ZeroDivisionError
print(f"\nLa velocidad es: {velocidad:.2f} m/s")
except ZeroDivisionError:
print("\nError: El tiempo no puede ser 0.")
finally:
print("\nCálculo finalizado.")
72
Programación en Python: Estadística a través de problemas resueltos
while numero != 0:
resto = numero % 10
salida = salida *10 + resto
numero = int(numero/10)
print (salida)
suma = 0
numero = -2000 # cualquier valor no 0
while numero != 0:
numero = int(input("Introduce el siguiente número "))
if ((numero % 2) == 0):
suma =suma + numero
print("suma", suma)
Para evitar la asignación inicial a un valor distinto de cero se puede utilizar lectura adelantada:
while numero != 0:
if ((numero % 2) == 0):
suma = suma + numero
numero = int(input("Introduce en siguiente número "))
print("suma", suma)
# Suma de cubos
n = int(input("Ultimo número a considerar:"))
sumaAcumulada = 0
i = 1
while i <= n :
c = i ** 3
sumaAcumulada = sumaAcumulada + c
i = i + 1
print("La suma es:", sumaAcumulada)
Bucle for
Un iterable es cualquier objeto en Python que se puede recorrer o iterar, es decir, que puede
devolver uno de sus elementos a la vez en un bucle. Ejemplos comunes de iterables son las listas,
las tuplas, los diccionarios y las cadenas de texto.
73
Programación en Python: Estadística a través de problemas resueltos
El bucle for se utiliza para tratar cada uno de los elementos de un objeto iterable y ejecutar el
bloque de código definido en el cuerpo del bucle por cada elemento del iterable, se suele llamar
recorrer un iterable. La condición del bucle está basada en el tamaño del iterable, lo que significa
que el bucle terminará cuando haya recorrido todos los elementos del objeto iterable.
En cada iteración elemento es la variable que toma el valor del miembro del iterable tratado ese
momento:
Si un objeto es iterable, puede usarse a la derecha de un bucle for. La variable elemento toma
automáticamente el valor de cada elemento en el iterable, eliminando la necesidad de acceder
manualmente a los valores mediante índices ([]).
Esta construcción supone una gran ventaja frente a otros lenguajes de programación que obligan
a la indexación en los recorridos, generando soluciones menos legibles. La sintaxis se asemeja
bastante al lenguaje natural pero en inglés, sería algo así como decir “para cada elemento llamado
c de cadena . . . ”. No todo es iterable en Python, pero las colecciones (lista, tuplas, conjuntos y
diccionarios) y las cadenas si lo son.
Por ejemplo, para recorrer una lista de palabras indicando cuantos caracteres tiene palabra en la
lista:
Finalmente, para tratar la lista de los días de la semana e indicar si es o no laborable, matizando
y animando en miércoles.
semana = ['Lunes','Martes','Miércoles','Jueves','Viernes','Sábado','Domingo']
Calcular la suma de los siguientes números 5, 8, 17, 12; e imprimir en pantalla el resultado.
74
Programación en Python: Estadística a través de problemas resueltos
suma = 0
mis_numeros = [5, 8, 17, 12]
Para evitar problemas si se va a modificar la lista sobre la que se itera en el cuerpo de bucle, puesto
que se trabaja con punteros a memoria, se debe generar una copia que sea la que se recorre.
Por ejemplo, si se quiere construir un programa que elimine los números pares de una lista dada:
posicion = 0
mis_numeros = [5, 8, 17, 12]
for numero in mis_numeros.copy():
if (numero %2) == 0:
mis_numeros.pop(posicion)
else:
posicion = posicion + 1
print(mis_numeros)
O bien construyendo una nueva lista resultado de las operaciones definidas en el cuerpo del bucle:
posicion = 0
mis_numeros = [5, 8, 17, 12]
nuevo = []
for numero in mis_numeros:
if (numero % 2) != 0:
[Link](numero)
print(nuevo)
Cuando no hay un iterable y se quiere hacer un for para ejecutar un número de veces las
instrucciones del cuerpo del bucle, pero sin que exista de partida un iterable, se debe tener una
variable contador que vaya cambiando (incrementándose o decrementándose) según la iteración
en la que se está, pero sin estar ligado a un iterable.
En Python se utiliza la función range para generar este contador. Cuando solo se proporciona un
argumento range devuelve una secuencia de números, incrementada en 1, comenzando desde 0
hasta el número anterior al argumento proporcionado.
75
Programación en Python: Estadística a través de problemas resueltos
Es posible hacer que el rango comience en otro número que no sea cero. Cuando se pasan dos
argumentos a la función range() se tiene el número inicial y el final de la secuencia (incluso con
valores negativos). Con un tercer argumento se fija el incremento de salto entre los elementos.
print(list(range(5,10))) # [5, 6, 7, 8, 9]
print(list(range(-50,-45))) # [-50, -49, -48, -47, -46]
print(list(range(-4,4))) # [-4, -3, -2, -1, 0, 1, 2, 3]
print(list(range(0,11,2))) # [0, 2, 4, 6, 8, 10]
print(list(range(-50,50,10))) # [-50, -40, -30, -20, -10, 0, 10, 20, 30, 40]
print(list(range(100,-10,-10))) # [100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0]
Para iterar sobre los índices de una secuencia, se pueden combinar range() y len().
frase = ['El', 'caballo', 'blanco', 'de', 'Santiago']
for i in range(len(frase)):
print(i, frase[i])
El objeto devuelto por range() se comporta como si fuera una lista range(10), pero no lo es.
Es un objeto que devuelve los elementos sucesivos de la secuencia indicada cuando se itera sobre
ella, de esta forma se ahorra espacio de memoria.
Si sólo se usa range para contar, es decir, no se necesita usar la variable contador en el cuerpo
de bucle, se puede usar el símbolo _ ahorando la necesidad de almacenar una variable. Si se quiere
escribr 5 veces “Hola Mundo”
for _ in range (0, 5) :
print("Hola Mundo")
Anidamiento de bucles
En el cuerpo de bucle puede aparecer cualquier instrucción o bloque de instrucciones, incluso otro
bucle. Este anidamiento implica que el bucle interior debe acabar antes de seguir con la siguiente
instrucción en el cuerpo del bucle exterior. Es decir, el bucle interior está totalmente incluido en el
bucle exterior.
Ejercicio resuelto: Cuadrado de 5 X 5 asteriscos
76
Programación en Python: Estadística a través de problemas resueltos
* * * * *
* * * * *
* * * * *
* * * * *
* * * * *
ancho=int(input("Ancho: "))
alto=int(input("Alto: "))
La instrucción break rompe el bucle sin importar cuántas repeticiones queden. Se usa típicamente
cuando se cumple una condición que hace innecesario seguir ejecutando el bucle. Por ejemplo, el
bucle siguiente busca números primos en el rango de 2 a 10:
Los “buenos” programadores no abusan de break porque no está disponible en todos los lenguajes.
Es mejor utilizar una variable booleana que marque la situación y se incorpore como condición al
bucle, utilizando un bucle while.
La instrucción continue salta a la siguiente iteración del bucle, omitiendo el resto del código del
cuerpo del bucle para esa iteración específica, y pasa a la siguiente iteración. Por ejemplo, cuando
se encuentra un número par, continue salta a la siguiente iteración sin ejecutar el print():
77
Programación en Python: Estadística a través de problemas resueltos
O para while:
i = 0
while i < 10:
i += 1
if i % 2 == 0:
continue # Salta los números pares
print(i)
La cláusula else se ejecuta cuando el bucle termina por agotamiento de la iteración (for), o
cuando la condición se vuelve falsa (while), pero no cuando se termina por una instrucción de
interrupción break que aquí se usa para indicar que el bucle terminó normalmente:
No obstante, esta solución hace dos lecturas con input (una dentro y otra fuera del bucle) y la
condición del bucle se “escribe en negativo”, es decir, recoge la condición no válida de los datos,
lo que dificulta la legibilidad del código. La alternativa es la utilización de un centinela/indicador
lógico que controle la validez. Es una extensión de la versión anterior pero incluyendo una variable
lógica que clarifica la notación, aunque se mantiene la duplicación de la lectura.
78
Programación en Python: Estadística a través de problemas resueltos
Otra alternativa, muy común en Python, es el uso de while True pero con salida abrupta
del bucle. Esta solución es el equivalente en Python del bucle repetir hasta que o do ...
while que está en otros lenguajes, no necesitando la salida incondicional porque no se usaría
break:
while True:
entero = int(input("Quiero un entero positivo "))
if entero <= 0: # condición de permanencia en bucle
print("dato erroneo")
else:
break
while True:
entero = int(input("Quiero un entero positivo "))
if entero > 0: # condición de salida del bucle
break
print("dato erroneo")
Esta solución no gusta demasiado a los programadores de otros lenguajes por el abuso de break.
Su utilización para principiantes se suele recomendar solo en estos bloques de lecturas de datos
con input, puesto que realmente ahorran líneas de código. En general, no se aconseja su uso
para operaciones de procesamiento, salvo por programadores avanzados.
Dentro de un bucle de lenctuar de datos con while True también se puede manejar la excepcion
ValueError:
while True:
try:
numero = int(input("Introduce un número: "))
print("El número introducido es:", numero)
break # Salir del bucle si se introduce un número válido
except ValueError:
print("Error: Por favor, introduce un número entero válido.")
Si también se quiere añadir una condición a la entrada, por ejemplo que el número sea un dígito
entre 1 y 49, el código sería:
while True:
numero_str = input("Introduce un número entre 1 y 49: ")
try:
numero = int(numero_str)
79
Programación en Python: Estadística a través de problemas resueltos
Una solución de validación de números sin manejar las excepciones puede usar [Link] :
while True:
numero_str = input("Introduce un número: ")
if numero_str.isdigit():
numero = int(numero_str)
print("El número introducido es:", numero)
break # Salir del bucle si se introduce un número válido
else:
print("Error: Por favor, introduce un número entero válido.")
Pero un real tiene parte decimal, que al leerse como una cadena con input debe controlarse:
while True:
try:
numero_real = float(input("Introduce un número real: "))
print("El número introducido es:", numero_real)
break # Salir del bucle si se introduce un número válido
except ValueError:
print("Error: Por favor, introduce un número real válido.")
O bien:
while True:
numero_str = input("Introduce un número real: ")
if all([Link]() or c == '.' for c in numero_str) \
and numero_str.count('.') <= 1:
try:
numero_real = float(numero_str)
print("El número introducido es:", numero_real)
break # Salir del bucle si se introduce un número válido
except ValueError:
print("Error: Por favor, introduce un número real válido.")
else:
print("Error: Por favor, introduce un número real válido.")
80
Programación en Python: Estadística a través de problemas resueltos
eleccion = "s"
valido = ("S", "s", "n", "N")
si_lista = ("S", "s")
81
Programación en Python: Estadística a través de problemas resueltos
print("Sobrepeso")
elif imc <= 35:
print("Obesidad clase I")
elif imc <= 40:
print("Obesidad clase II")
else:
print("Obesidad clase III")
while True:
try:
cantidad = float(input("¿Cantidad a invertir? "))
if cantidad > 0:
break
else:
print("La cantidad a invertir debe ser un número positivo.")
except ValueError:
print("Entrada no válida. Ingresa un número válido.")
while True:
try:
interes = float(input("¿Interés porcentual anual? "))
# No es necesario validar que sea positivo,
# ya que un interés negativo puede representar pérdidas
break
except ValueError:
print("Entrada no válida. Ingresa un número válido.")
while True:
try:
tiempo = int(input("¿Años? "))
if tiempo > 0:
break
else:
print("El número de años debe ser un entero positivo.")
except ValueError:
print("Entrada no válida. Ingresa un número entero positivo.")
Tal como se muestra a veces la validación son muchas más líneas de código que los cálculos a
realizar. Esta es la razón por la que debe quedar claro el contrato de uso de un script, es decir,
con qué tipo de datos y en qué circunstancias se generará un resultado (correcto o no) sin dar
errores en la ejecución o bloquearse.
82
Programación en Python: Estadística a través de problemas resueltos
*
**
***
****
Solución inicial
while True:
try:
n = int(input("Introduce la altura del triángulo (entero positivo): "))
if n > 0:
break # Si es un entero positivo, salimos del bucle
else:
print("Por favor, introduce un número entero positivo.")
except ValueError:
print("Entrada no válida. Debes ingresar un número entero positivo.")
for i in range(n):
for _ in range(i + 1):
print("*", end="")
print("")
Otra solución
83
Programación en Python: Estadística a través de problemas resueltos
if i == n:
print(str(n) + " es primo")
else:
print(str(n) + " no es primo")
print("-------------------------------------------------------")
print("Calcula la suma de divisores.")
print("-------------------------------------------------------")
#Entradas
print("Introduce un número, y para acabar uno negativo:") # Ojo con el 0
numero = int( input("Núm: "))
84
Programación en Python: Estadística a través de problemas resueltos
if numero % i == 0 :
suma = suma + i
print("\nSALIDA: ")
print("----------------------------------------------------")
print("La suma de los divisores del número es:", suma, "\n" )
print("Introduce un número, y para acabar uno negativo:")
numero = int( input("Núm: "))
Ejercicio resuelto: Algoritmo para la determinación del máximo común divisor (MCD).
Se trata de encontrar el mayor número que divida exactamente dos números enteros positivos
dados. El algoritmo que resuelve el problema es uno de los más antiguos y famosos estando
atribuido a Euclides (no comprobar la posibilidad de valores no numéricos).
Pasos del algoritmo:
Entrada: Dos números a y b, con a > b.
Mientras b distinto 0 se calcula el resto r= a % b y se reemplaza a por b y b por r. Cuando b=0,
el MCD es el valor actual de a.
# Halla el MCD de dos números enteros naturales, incluido el 0
# ENTRADA DE DATOS
print('Introduzca dos enteros, para los que se calculará el MCD.')
dividendo = -1
divisor = -1
while dividendo < 0 or divisor < 0:
dividendo = int(input('Introduzca el primer número (>=0): '))
divisor = int(input('Introduzca el segundo número (>=0): '))
# ALGORITMO MCD
if dividendo < divisor: # Asegurando dividendo sea mayor que divisor
dividendo, divisor = divisor, dividendo
if dividendo == divisor == 0:
mcd = 0
elif divisor == 0:
mcd = dividendo
else:
resto = dividendo % divisor # resto adelantado fuera del bucle
while resto != 0:
dividendo = divisor
divisor = resto
resto = dividendo % divisor
mcd = divisor
# SALIDA .
print(f'El MCD de {dividendo_copia} y {divisor_copia} es {mcd}.')
85
Programación en Python: Estadística a través de problemas resueltos
Con la función se crea un objeto enumerador, que devuelve pares de valores indice, valor
comenzando por 0. Con el for se desestructuran los pares que en este ejemplo solo se muestran
con print pero podrían tratarse de cualquier otra manera.
reversed
Para iterar sobre una secuencia en orden inverso, se especifica primero la secuencia sin invertir y
luego se llama a la función reversed().
86
Programación en Python: Estadística a través de problemas resueltos
sorted
Se utiliza la función sorted() para iterar sobre una secuencia ordenada s puesto que devuelve
una nueva lista ordenada dejando a la original intacta.
zip
Si se usan varias listas con zip, el resultado devuelto será una tupla donde cada elemento tendrá
todos y cada uno de los elementos i-ésimos de las listas dadas como entrada a zip. Combinada
con un for para iterar listas permite recorrer estas listas en paralelo:
a = [1, 2]
b = ["Uno", "Dos"]
La salida es
Número 1 Letra Uno
Número 2 Letra Dos
Este versatilidad se verá sobre un ejemplo donde se tienen que guardar los datos de un agricultor:
nombre, el municipio y el total de hectáreas de su explotación. Se puede pensar en tratar los datos
de cada agricultor como una lista (["Miguel", .Almería", 1200]), por lo que los datos de
todos los agricultores serán una lista de listas.
87
Programación en Python: Estadística a través de problemas resueltos
El uso de tres listas combinado con zip() mejora la legibilidad y mantenibilidad porque manejar
el anidamiento puede ser complejo. Si bien, si se quiere tener la lista completa, se puede crear
utilizando list.
print(*etiquetas,sep=" | ")
for nom, munic, sup in zip(nombres, municipios, superficies):
print("-"*47)
print(f"{nom:>8s} | {munic:ˆ13} | {sup:>13.2f}")
print("-"*47)
iter
La función iter() se utiliza para crear un iterador a partir de un objeto iterable. Un iterador
permite recorrer los elementos de un iterable sin necesidad de usar un bucle for. En lugar de
almacenar toda la secuencia en memoria, los iteradores generan los elementos bajo demanda,
optimizando el uso de recursos.
Una vez que se tiene un iterador, se pueden obtener sus elementos uno a uno utilizando la función
next():
texto = "python"
iterador = iter(texto) # conversión en un iterador
print(next(iterador)) # 'p'
print(next(iterador)) # 'y'
print(list(iterador)) # ['t', 'h', 'o', 'n']
Un iterador también puede usarse en un bucle, sin necesidad de llamar manualmente a next():
iterador = iter(texto)
Una vez que se han recorrido todos los elementos de un iterador, este queda vacío y no puede
reutilizarse, generándose una excepción StopIteration, ya que no quedan elementos para iterar.
88
Programación en Python: Estadística a través de problemas resueltos
Comprensión de listas
La comprensión de listas permite crear listas de forma compacta y eficiente. Se usa con corchetes
[ ], incluyendo una expresión seguida de un for, y opcionalmente, condiciones if o bien, más
for. Esto permite generar listas de manera más clara y concisa.
La salida es:
[0, 4, 16, 36, 64, 100, 144, 196, 256, 324, 400, 484, 576, 676, 784, 900, 1024, 1156, 1296, 1444,
1600, 1764, 1936, 2116, 2304]
El bucle for genera el resultado correcto, pero la comprensión de listas es una notación mucho
más compacta y legible. Por ejemplo, para convertir en mayúscula los elementos de una lista dada:
texto_minuscula = ['Un','día','vi','una','vaca','vestida','de','uniforme']
O si se quiere transformar esta lista en esta otra lista con los valores al cuadrado:
xs = [0, 1, 2, 3, 4, 5]
Otra de las ventajas de la comprensión de listas es que se pueden aplicar condicionales tanto en el
iterador como en los valores o en ambos:
89
Programación en Python: Estadística a través de problemas resueltos
Dado un conjunto de árboles frutales con los registros de producción en kilogramos, se quiere
calcular el promedio de peso de frutos por árbol.
Construir un programa para calcular la media de la cosecha diaria de tomates mayores o iguales a
25 kg de la siguiente lista [25, 32, 20, 18, 30, 35, 24]
cosechas_mas_25 = []
for cosecha in cosechas_diarias:
if cosecha >= 25:
cosechas_mas_25.append(cosecha)
suma_cosechas = sum(cosechas_mas_25)
cantidad_cosechas = len(cosechas_mas_25)
Una alternativa más eficiente al bucle de obtener las cosechas mayores o iguales a 25 kg sería
utilizar range para iterar sobre los índices puesto que no tiene que realizar busqueda secuencial
90
Programación en Python: Estadística a través de problemas resueltos
for i in range(len(cosechas_diarias)):
if cosechas_diarias[i] >= 25:
cosechas_mas_25.append(cosechas_diarias[i])
espacio_producto = []
for m in moneda:
for d in dado:
espacio_producto.append((m, d))
print(espacio_producto)
Iterar en un diccionario
Si bien los diccionarios son iterables, puesto que usan pares clave-valor se puede iterar de tres
modos, sobre las claves, los valores, y los pares clave-valor llamando a métodos específicos de
diccionarios (.value() y .items()).
Por ejemplo:
91
Programación en Python: Estadística a través de problemas resueltos
for clave, valor in [Link](): # Para iterar sobre los pares clave-valor
print(clave, valor)
print (clave)
Recorrido de un diccionario
for t in [Link]():
print(t[0], end= " ")
print(t[1])
# Si sólo se usa una variable, se crea una tupla con el par clave-valor
for par in [Link]():
print(par)
total_ventas = 0
dias_ventas = 0
Unidades Coste
0-10 20
11-25 15
mas de 26 12
92
Programación en Python: Estadística a través de problemas resueltos
El programa debe generar el resultado correcto en todos los casos de prueba para ser correcto.
Esto se puede hacer de forma manual ejecutando el programa tantas veces como casos de prueba
y comprobando los resultados, o bien usando algún tipo de software que gestione estas pruebas de
unidad.
93
Programación en Python: Estadística a través de problemas resueltos
Pytest es un marco de pruebas (testing framework) en Python que se utiliza para escribir y ejecutar
tests de manera eficiente. Es especialmente útil para realizar pruebas automatizadas de funciones,
módulos y aplicaciones en Python. Es fácil de utilizar porque todos los .py que contienen la palabra
test se lanzan para validar el código. Lo habitual es tener una carpeta llamada test donde se
colocarán todos los programas de prueba. Por ejemplo, el script [Link]:
a=1
b=2
Tendrá asociado un script de prueba llamado por ejemplo “asigna_test.py“‘ que se lanzará con
pytest cada vez que se realice una prueba.
import asigna as caso
def test_asigna():
assert caso.a == 1
assert caso.b == 2
Pero en este libro no se definirán ni usarán sistemas automáticos de prueba en los ejercicios
resueltos.
Prácticamente todos los programas se modificarán en el futuro. Un programa será claro si su
contenido es entendible por personas distintas a su autor o por el mismo autor pasado un tiempo.
Sobre el programa anterior se puede observar como el uso de comentarios, la separación en tres
areas: Entrada - Proceso - Salida, y la utilización de idenficadores con significado para el problema
mejoran la claridad. El mismo ejemplo menos claro sería:
if dd < 0 or d < 0:
print("Ambos números deben ser iguales o mayores que 0.\n")
ddd = dd
dddd = d
if dd < d:
dd, d = d, dd
if dd == d == 0:
a = 0
elif d == 0:
a = dd
else:
b = dd % d
while b != 0:
dd = d
d = b
94
Programación en Python: Estadística a través de problemas resueltos
b = dd % d
a = d
print(f'El MCD de {ddd} y {dddd} es {a}.')
# ENTRADA DE DATOS
print('Introduzca dos enteros, para los que se calculará el MCD.')
if dividendo == divisor == 0:
mcd = 0
elif divisor == 0:
mcd = dividendo
else:
resto= divisor
while divisor > 0 and resto != 0:
resto = dividendo % divisor
dividendo = divisor
divisor -= 1 # Desciende de 1 en 1
mcd = dividendo
# SALIDA
print(f'El MCD de {dividendo_copia} y {divisor_copia} es {mcd}.')
La facilidad de uso para el usuario define la amigabilidad. En ciertos casos puede producir
ineficiencia y se ha llegar a un compromiso. Para scripts simples basta con mostrar la información
al usuario que facilite la interacción, por ejemplo mostrando mensajes, bien sea en cada orden de
entrada o para explicar los resultados.
Se dice que un programa es robusto si es capaz de reaccionar ante entradas no definidas o al
menos generar mensajes de aviso sin generar bloqueo. Este criterio se relaja en ciertas condiciones,
dependiendo del contrato/compromiso de uso fijado al contruir el código, basta con recordar los
ejemplos de los bucles de validación de las entradas de un programa o los bloques try.
# ENTRADA DE DATOS
print('Introduzca dos enteros, para los que se calculará el MCD.')
dividendo = -1
divisor = -1
while dividendo < 0 or divisor < 0:
dividendo = int(input('Introduzca el primer número (>=0): '))
95
Programación en Python: Estadística a través de problemas resueltos
O bien:
O incluso:
while True:
entero = int(input('Introduzca un entero positivo: '))
if entero >=0:
break
96
Funciones. Diseño Modular.
En Python, al igual que en otros lenguajes de programación, una función es un bloque de código
con nombre que encapsula un conjunto de instrucciones diseñadas para realizar una tarea específica.
Su principal ventaja es la reutilización, puesto que una función puede ejecutarse tantas veces
como sea necesario simplemente llamándola por su nombre. Además, su comportamiento puede
adaptarse mediante argumentos, que permiten modificar su ejecución en función del contexto.
Además de permitir la creación de funciones personalizadas, Python ofrece una gran cantidad de
funciones integradas, así como muchas otras organizadas en librerías o paquetes. Por ejemplo, al
importar una librería completa con import math, se tiene acceso inmediato a todas sus funciones,
que pueden usarse a lo largo del código. Sin embargo, si solo se necesita una función específica
(como sqrt para calcular raíces cuadradas), se puede usar una importación más precisa con from
math import sqrt. Esta forma no solo hace el código más limpio y directo, sino que también
evita cargar funciones innecesarias en memoria.
En el contexto de las funciones, se utilizan dos acciones clave: definir y llamar una función:
Definir una función implica especificar las instrucciones que se van a reutilizar, junto con los
datos necesarios para su ejecución. A este proceso también se le llama declarar o construir
una función.
Llamar a una función ocurre cuando se utiliza dicha función en el código, identificándola por
su nombre y proporcionando los valores concretos que procesará en ese momento. También
se conoce como invocar, lanzar o ejecutar la función.
97
Programación en Python: Estadística a través de problemas resueltos
Algunas funciones tienen parámetros con valores por defecto; por ejemplo, sep en print tiene
como valor predeterminado '\n', lo que hace que cada impresión ocurra en una nueva línea.
Cuando una función define sus argumentos con nombres, éstos pueden especificarse en cualquier
orden al llamarla, siempre que se indiquen sus claves explícitamente.
# son equivalentes no importa el orden
print('Hola', 'que', 'tal', sep=' ', end='!\n')
print('Hola', 'que', 'tal', end='!\n', sep=' ')
Python recomienda que cualquier código esté dentro de una función incluso cuando el script
tiene un solo bloque de instrucciones. Eso sí, hay que de decirle al intérprete por donde tiene que
empezar la ejecución para lo que se utiliza un nombre especial __main__, tal como se mostró en
el script para calcular la velocidad.
Para definir una función se utiliza la palabra clave def, seguida del nombre que se quiere dar
a la función que se ha de formar con las mismas normas que los identificadores de variables. A
continuación, se añaden los paréntesis con los parámetros de entrada de la función seguidos de
dos puntos (:) que marcan el inicio de un bloque. Después, se agregan las instrucciones o bloques
de instrucciones que se quiere que se ejecuten cada vez que se invoque a la función. Dentro de
este bloque de instrucciones (casi siempre al final y de forma opcional, aunque recomendable) se
coloca una instrucción de retorno usando la palabra reservada return que indica el valor devuelto
por la función. Además, siempre que sea posible, se deben definir valores por defecto default
para todos los parámetros de entrada.
Si bien en otros lenguajes no siempre un bloque de código devuelve un valor (módulos o pro-
cedimientos), en Python una función siempre devolverá un valor, bien se puede hacer de forma
explicita utilizando return o bien devolviendo el valor predeterminado None (como el caso de
print).
Puesto que la indentación es fundamental para definir los bloques de código, es importante
asegurarse de que todas las líneas que forman parte de una función estén correctamente indentadas.
98
Programación en Python: Estadística a través de problemas resueltos
Por ejemplo, para construir una función llamada mifuncion que devuelva una cadena con un
saludo se construye el siguiente código:
# Definición
def mifuncion():
rsltd = '¡Hola!'
return rsltd
Para utilizar la función desde cualquier otro bloque de código, se emplea el nombre mifuncion
que puede ser otra función.
# Llamada
print(mifuncion())
print(mifuncion())
print(mifuncion())
# con esto se muestra tres veces: ¡Hola!
99
Programación en Python: Estadística a través de problemas resueltos
# Ejemplos de uso
print(contardigitos("abc123")) # Salida: 3
print(contardigitos()) # Salida: 4 (porque "1111" tiene 2 dígitos)
cadena = input()
print(contardigitos(cadena))
En el código se puede ver que la función necesita un dato de entrada, representado por la variable
s, para realizar el conteo de dígitos. Al llamar a la función, éste dato se reemplaza por el valor
específico que se desea analizar, como en el caso de la variable cadena. Si no se proporciona
ningún valor al llamar a la función, se utilizará el valor predeterminado “1111”.
Ejercicio resuelto: Fibonacci Construir una función que calcule los N primeros números de la
sucesión de Fibonacci. La función recibirá un solo argumento N, y devolverá una lista de los
primeros N números de Fibonacci:
def fibonacci(N):
L = []
a, b = 0, 1
while len(L) < N:
a, b = b, a + b
[Link](a)
return L
Se puede devolver cualquier objeto de Python, simple o compuesto, lo que significa que las
construcciones que pueden ser difíciles en otros lenguajes son sencillas en Python. Por ejemplo,
cuando se quieren devolver múltiples valores simplemente se colocan en una tupla, que se construye
con comas:
def real_imag_conj(val):
return [Link], [Link], [Link]()
r, i, c = real_imag_conj(3 + 4j)
print(r, i, c)
a=real_imag_conj(3 + 4j)
type(a)
En resumen, un programa Python son por tanto un conjunto de funciones que se comunican entre
ellas, bien sea dentro del mismo script/archivo, o llamado a funciones que están en otros scripts o
librerías importadas.
El bloque principal puede explicitarse como una función, habitualmente llamada main(), y asignar
__main__, aunque en los scripts de iniciación como los de este libro no se recomienda. El resultado
para el caso de la sucesión de Fibonacci sería:
100
Programación en Python: Estadística a través de problemas resueltos
def fibonacci(N):
L = []
a, b = 0, 1
while len(L) < N:
a, b = b, a + b
[Link](a)
return L
def main():
N = -1 # Valor inicial no válido
while N <= 0:
N = int(input("Ingrese un número positivo: "))
L = fibonacci(N)
print(L)
if __name__ == "__main__":
main()
premiados = []
101
Programación en Python: Estadística a través de problemas resueltos
Aquí se pone de manifiesto una cuestión importante para el uso de funciones. Antes de su llamada
tienen que estar “disponibles”, es decir, definida antes en el archivo .py, igual que se hace con
las instrucciones de importación, porque al importar se hacen visibles las funciones del paquete
importado en el script actual, razón por la que se colocan los import al principio de los scripts.
Importación
La palabra clave import se utiliza para acceder al contenido de otro script o módulo. Cuando las
funciones se encuentran distribuidas en diferentes archivos, scripts o librerías, es necesario realizar
una importación antes de hacer uso de cualquier función o elemento del módulo importado.
Ejercicio resuelto: Función máximo
Construir una función de devuelva el máximo de dos números.
x = 33
y = 77
print( mayor(x, y))
print( mayor(y, x))
h = 12
j = 22
d = mayor(h, j)
print(d)
Si estos dos bloques de código están en el mismo archivo. No hay problema porque mayor es
visible siempre que aparezca antes de la llamada.
102
Programación en Python: Estadística a través de problemas resueltos
Pero si no es así hay que cargar la función para que esté disponible, es decir para que el módulo
principal vea la función, es necesario incorporarlo. Para usarlo se utilizará la notación punto ..
Hay dos opciones siendo [Link] el script que contiene la función mayor:
a = int(input("primero: "))
b = int(input("segundo: "))
print(mayor(a, b))
O bien:
import maximo
a = int(input("primero: "))
b = int(input("segundo: "))
print([Link](a, b))
a = int(input("primero: "))
b = int(input("segundo: "))
print([Link](a, b))
103
Programación en Python: Estadística a través de problemas resueltos
def saludar():
print("Hola, mundo")
import saludo
[Link]()
Lo que ocurre es que al importar todo el módulo cuando se ejecuta el importador lo primero que
se hace es ejecutar el comportamiento que no está bajo ninguna función en el módulo importado
(el bloque principal).
La construcción if __name__ == "__main__": permite ejecutar código solo cuando el archivo
se ejecuta directamente, y no cuando es importado como un módulo en otro archivo. De esta
forma el script de saludo, archivo [Link], se modificaría de esta forma:
def saludar():
print("Hola, mundo")
if __name__ == "__main__":
saludar() # solo se ejecuta si el archivo es ejecutado directamente
import saludoconmain
[Link]()
def saludar(nombre):
print("Hola ",nombre)
104
Programación en Python: Estadística a través de problemas resueltos
saludar('Juan')
for i in range (1,4):
saludar('Carlos')
if __name__ == "__main__":
main()
Sin embargo, para mantener la simplicidad, en la mayoría de los ejercicios de este libro no separara
la lógica de importación de la ejecución, que es lo que permitiría que un archivo .py funcione
tanto como un script ejecutable como un módulo importable. La parte principal del script no se
muestra bajo una función sino en el nivel más alto de la indentación, siendo aquí donde empieza
la ejecución.
def var(t):
media = sum(t) / float(len(t))
suma2 = sum(x**2 for x in t)
var2 = suma2 / float(len(t)) - media**2
return(var2)
105
Programación en Python: Estadística a través de problemas resueltos
def incrementar(valor):
valor += 1
print("Dentro de la función:", valor)
x = 10
incrementar(x)
print("Fuera de la función:", x)
La salida es:
Dentro de la función: 11
Fuera de la función: 10
Aquí, x no cambia fuera de la función. Esto se debe a que x es un entero (un objeto inmutable).
Aunque la función recibe una referencia a x, cuando se hace valor += 1, se crea otro objeto y
se asigna a valor, por lo que el objeto original no se modifica. Lo mismo pasa en el siguiente
caso.
def doblar_valor(numero):
numero *= 2
return numero
n = 10
print(doblar_valor(n))
print(n)
Para modificar los tipos simples hay que devolverlos modificados y reasignarlos.
n = doblar_valor(n)
print(n)
mi_lista = [1, 2, 3]
agregar_elemento(mi_lista)
print("Fuera de la función:", mi_lista)
La salida sería:
Dentro de la función: [1, 2, 3, 4]
Fuera de la función: [1, 2, 3, 4]
ns = [10,50,100]
print(doblar_valores(ns))
print(ns)
106
Programación en Python: Estadística a través de problemas resueltos
En el caso de los tipos compuestos mutables, se puede evitar la modificación enviando una copia
del objeto mutable:
def doblar_valores(numeros):
for i,n in enumerate(numeros):
numeros[i] *= 2
return numeros
Sin embargo, ahora la función permite explorar nuevos escenarios, como por ejemplo el efecto de
usar diferentes valores iniciales:
fibonacci(10, 0, 2)
Además, los valores también se pueden especificar por nombre. Al hacerlo, el orden de los
parámetros no importa, lo que hace que la llamada a la función sea aún más flexible:
fibonacci(10, b=3, a=1)
107
Programación en Python: Estadística a través de problemas resueltos
de variables, así como sus ventajas y desventajas. Es importante recordar que el uso de variables
globales está desaconsejado en la mayoría de los casos.
Una variable global es aquella que se declara fuera de cualquier función y, por lo tanto, es
accesible en todo el script, incluidas todas las funciones, a menos que haya una redefinición local
dentro de una función. Para los ejemplos de este apartado se usará if __name__ == "__main__"
para delimitar claramente los bloques de código.
x = 10 # Variable global
def imprimir_x():
print(x) # Accede a la variable global
if __name__ == "__main__":
imprimir_x() # Salida: 10
print(x)
En este código, la variable x es global porque se ha asignado valor fuera de cualquier función.
Cualquier bloque dentro del programa puede acceder a x, siempre y cuando no la sobrescriba
dentro de su propio ámbito.
Una de las ventajas de las variables globales es que cualquier función o parte del código puede
acceder a ellas sin tener que pasarlas como parámetros. Además, mantienen su valor durante toda
la ejecución del programa. Se ahorra tener que incorporarla como argumento si se usa en múltiples
llamadas a funciones.
El principal inconveniente es, al poderse cambiar en cualquier parte del código, que es difícil
rastrear los problemas que las involucran. Pueden aparecer efectos secundarios si varias funciones
modifican una variable global, al producirse cambios inesperados que afectarán a otras partes del
programa.
Una variable local es aquella a la que se asigna valor dentro de una función, y sólo es accesible
dentro de esa función. Fuera de ella, no es visible ni accesible.
def mi_funcion():
y = 5 # Variable local
print(y)
if __name__ == "__main__":
mi_funcion() # Salida: 5
print(y) # Error: 'y' no está definida fuera de la función
108
Programación en Python: Estadística a través de problemas resueltos
Uno de sus inconvenientes es que se requerirán más parámetros. Si es necesario que varias funciones
compartan un dato, debe pasarse como argumento a cada función, lo que puede aumentar la
complejidad de la llamada.
Si bien las variables globales son accesibles dentro de las funciones, modificarlas directamente
dentro de una función requiere el uso de la palabra clave global. De lo contrario, Python creará
una nueva variable local con el mismo nombre, sin modificar la global. Esto previene efectos no
deseados sobre la variable global, pero supone otro gran problema para la depuración del código.
x = 10 # Variable global
def modificar_x():
global x
x = 20 # Modifica la variable global
if __name__ == "__main__":
modificar_x()
print(x) # Salida: 20
x = 10
def modificar_x():
x = 20 # Crea una variable local con el mismo nombre
print(x) # Salida: 20 (solo local)
modificar_x()
print(x) # Salida: 10 (global no modificada)
Como recomendación es mejor usar variables globales con moderación o incluso no usarlas para
evitar complicaciones en la depuración y mantenimiento del código. En lugar de depender de
variables globales, se recomienda pasar los valores necesarios como argumentos a las funciones y
devolver los resultados, lo que hace que el código sea más limpio, predecible y reutilizable.
Funciones importables
Desde el momento en que se importa o se define una función está disponible para ser usada. Si se
quiere saber cuáles son todas las funciones propias de Python se usa dir():
dir(__builtins__)
Al usar dir sin argumentos, se devuelve una lista con los nombres de funciones del ámbito
actual. Aunque esto puede tener poco sentido dentro de un script, resulta muy útil cuando se
está ejecutando Python en modo interactivo. Además, si se ha importado un paquete y se pasa el
nombre del paquete como argumento, se muestran las funciones disponibles en dicho paquete ,
109
Programación en Python: Estadística a través de problemas resueltos
como en el caso de dir(math). No obstante, se puede encontrar una lista exhaustiva de todas
las funciones y de las funciones de las librerías estándar en la documentación oficial de Python.
Para conocer los detalles de cualquier función hay disponible una funcion de ayuda:
help(max)
Es posible incorporar ayuda específica a las funciones definidas por el programador utilizando los
comentarios multilínea.
Ejercicio resuelto: Función media
Función que obtiene la media de una lista pasada como argumento
def media(t):
"""Función que devuelve la media de la lista pasada como argumento"""
return(float(sum(t)) / len(t))
lista=[1, 2, 3, 4, 5]
print(media(lista))
media(t)
Función que devuelve la media de la lista pasada como argumento
>>>
Las expresiones lambda en Python son una alternativa concisa para definir funciones anónimas.
Se utilizan cuando se necesita una función pequeña y rápida sin necesidad de asignarle un nombre
formal. Aunque tienen limitaciones en cuanto a la cantidad de expresiones que pueden contener,
son útiles en situaciones específicas, como la manipulación de listas. Así la función media del
ejemplo anterior se reescribiría como:
lista = [1, 2, 3, 4, 5]
print(media(lista))
lambda t: float(sum(t)) / len(t) define una función anónima que calcula la media. Se
usa sum(t) para sumar los elementos y len(t) para obtener la cantidad de elementos. Se
convierte el resultado a float para asegurar que sea decimal en divisiones exactas.
110
Programación en Python: Estadística a través de problemas resueltos
seguridad en los sistemas informáticos, la capacidad de obtener valores impredecibles es clave para
el desarrollo de soluciones eficientes y robustas.
Los números aleatorios están estrechamente relacionados con la estadística porque muchos métodos
estadísticos dependen de la aleatoriedad para modelar fenómenos reales y hacer inferencias sobre
poblaciones. En el muestreo aleatorio, por ejemplo, se seleccionan datos de manera imparcial
para garantizar que las conclusiones sean representativas. La aleatoriedad también es clave en
pruebas de hipótesis y en la generación de distribuciones de datos sintéticos, lo que permite evaluar
modelos y tomar decisiones informadas basadas en probabilidades.
Para generar números pseudo-aleatorios en Python se hace uso del módulo random de la librería
estándar. Este módulo ofrece una serie de funciones que generan números aleatorios de maneras
diferentes.
También existen algunas funciones para generar de datos siguiendo ciertas distribuciones estadísticas
y otras que requieren un conocimiento estadístico más allá de la programación en Python.
La obtención de números pseudo-aleatorios de valor entero, se hace con la función randint(). La
función randint(a, b) devuelve un número entero comprendido entre a y b (ambos inclusive)
de forma aleatoria. Por ejemplo, se puede utilizar para determinar quién comienza una partida
(jugador/PC); simular el dado del parchís, etc. . .
import random
# ¿Quién comienza?
comienza = [Link](0, 1)
if comienza == 0:
print('Comienza el jugador')
else:
print('Comienza el PC')
[Link](5,27,4)
[Link]()
[Link](0,1)
111
Programación en Python: Estadística a través de problemas resueltos
import random
def lanzamiento_moneda():
resultado = [Link](0, 1)
if resultado == 0: # Devolver "cara" si es 0, "cruz" si es 1
return "cara"
else:
return "cruz"
Una alternativa a la solución anterior, con menos código y más rápida al generar la lista en un
solo paso, es usar [Link]().
import random
Método Descripción
seed(n) Inicia una secuencia de números aleatorios con la semilla n (entero
positivo)
choice(lista) Elige aleatoriamente un elemento de lista
choices(lista, k=n) Devuelve una lista de n elementos seleccionados aleatoriamente
con reemplazo
shuffle(lista) Desordena aleatoriamente los elementos de lista (modifica la
lista original)
sample(lista, k=n) Devuelve una muestra de n elementos diferentes de lista (sin
reemplazo)
randint(a, b) Devuelve un número entero aleatorio entre a y b (incluidos)
112
Programación en Python: Estadística a través de problemas resueltos
Método Descripción
randrange(a, b, s) Devuelve un número entero aleatorio en el rango [a, b), con
paso s
uniform(a, b) Devuelve un número flotante aleatorio entre a y b
random() Devuelve un número flotante aleatorio en el rango [0.0, 1.0)
import random
import random
def lanzamiento_dado_cargado():
resultado = [Link](0, 1)
113
Programación en Python: Estadística a través de problemas resueltos
import random
114
Programación en Python: Estadística a través de problemas resueltos
# Generar 1000 números aleatorios que sigan una distribución normal de media
# 5 y desviación estándar 2. normalvariate(5, 2).
Un script que combina los distintos métodos y que muestra cómo utilizarlos es el siguiente:
import random
115
Programación en Python: Estadística a través de problemas resueltos
def comprobar(entero):
if entero > 0:
return True
else:
return False
def dameentero(entero):
if entero > 0:
return entero
else:
return False
while True:
entero = dameentero(int(input("Quiero un entero positivo ")))
if entero:
break
print("dato erroneo")
print("aqui proceso el entero:", entero)
En estas funciones de lectura y validación de datos se pueden incorporar bloques try para gestionar
la excepcion de error y si es preciso devuelve el entero.
116
Programación en Python: Estadística a través de problemas resueltos
def dameentero(entero):
if entero > 0:
return entero
else:
raise ValueError
while True:
try:
entero =dameentero(int(input("Quiero un entero ")))
if entero>0:
break
except ValueError:
print("dato erroneo")
print("aqui proceso el entero:", entero)
Otro ejemplo es una función dentro de la cual se lee un entero asegurándose que está en un rango
y que si no es correcto devuelve None.
117
Programación en Python: Estadística a través de problemas resueltos
while True:
numero = int(input('Deme un entero positivo mayor que 1: '))
if es_primo(numero):
print(f'El número {numero} es primo.')
else:
print(f'El número {numero} no es primo.')
opcion = input("Desea salir (s/n):")
if opcion == 's' or opcion == 'S':
break
Crear una función que devuelva una lista resultado del producto cartesiano de las dos listas pasadas
como argumento.
lista1 = [1, 2, 3, 4]
lista2 = ['a', 'b', 'c']
producto_cartesiano(lista1, lista2)
118
Programación en Python: Estadística a través de problemas resueltos
Se debe construir una función que calcule el factorial a la que se llamará tres veces para calcular
el combinatorio. Hay identifcar que el factorial es la tarea que se repite y para la que se debe
contruir una función que devuelve un valor.
def factorial(n):
"""Calcula el factorial de un número de forma iterativa."""
resultado = 1
for i in range(1, n + 1):
resultado *= i
return resultado
# Pedir y validar n y m
n = int(input("Introduce el valor de n: "))
while n < 0:
print("n debe ser un número entero no negativo.")
n = int(input("Introduce el valor de n: "))
m = int(input("Introduce el valor de m: "))
while m < 0 or m > n:
print("m debe ser un número entero no negativo y no mayor que n.")
m = int(input("Introduce el valor de m: "))
En este caso, la validación de la entrada está mezclada con el cálculo en sí mismo y con la
visualización de los datos. Sin embargo, si los datos se obtuviesen de otra manera diferente, la
separación en entrada - proceso - salida facilitaría el mantenimiento del código, permitiendo otra
forma de utilizar las funciones.
def factorial(n):
"""Calcula el factorial de un número de forma iterativa."""
resultado = 1
for i in range(1, n + 1):
resultado *= i
return resultado
def leer_datos():
"""Solicita al usuario los valores de n y m con validaciones."""
while True:
try:
n = int(input("Introduzca el número de objetos (n): "))
if n > 0:
break
119
Programación en Python: Estadística a través de problemas resueltos
else:
print("Error: Debe ser un número positivo.")
except ValueError:
print("Error: Debe ingresar un número entero.")
while True:
try:
m = int(input(f"Introduzca m (1-{n}): "))
if 1 <= m <= n:
break
else:
print(f"Error: El número debe estar en el rango [1, {n}].")
except ValueError:
print("Error: Debe ingresar un número entero.")
return n, m
while True:
print("\nCOMBINACIONES DE n OBJETOS TOMADOS DE m EN m")
print("============================================\n")
n, m = leer_datos()
combinatorio = factorial(n) / (factorial(m) * factorial(n - m))
Escribir una función que permite buscar los números que faltan en una lista.
def encuentra_numeros(nums):
"""Función que dada una lista de enteros devuelve los que
faltan entre el máximo y el mínimo
"""
min_num = min(nums)
max_num = max(nums)
faltan = []
for i in range(min_num, max_num + 1):
if i not in nums:
[Link](i)
return faltan
nums = [0, 2, 3, 6]
faltan = encuentra_numeros(nums)
print(faltan)
120
Programación en Python: Estadística a través de problemas resueltos
def palabras_repetidas(texto):
"""Función que indica cuantas y cuales
son las palabra duplicadas en un texto"""
palabras = [Link]().split() # (sin distinguir mayúsculas)
vistas = set()
repetidas = set()
for palabra in palabras:
if palabra in vistas:
[Link](palabra)
else:
[Link](palabra)
return repetidas
# Ejemplo de uso
texto = "el sol brilla en el cielo y el cielo es azul"
print(palabras_repetidas(texto)) # {'el', 'cielo'}
# Funciones de entrada
def v_lee_vector():
return [float(input(f'Componente {com}: ')) for com in ['x', 'y', 'z']]
# Funciones de cálculo
def v_suma(u, v):
return [ u[0] + v[0], u[1] + v[1], u[2] + v[2] ]
def menu() :
opcion=0
121
Programación en Python: Estadística a través de problemas resueltos
while opcion != 5 :
opcion = int(input("Elige una opción: "))
if opcion == 1 :
print("La suma de los vectores es ",v_suma(u,v))
if opcion == 2 :
print("El producto escalar de los vectores es ",\
v_producto_escalar(u,v))
if opcion == 3 :
print("El producto vectorial de los vectores es ",\
v_producto_vectorial(u,v))
if opcion == 4 :
if v_producto_escalar(u,v)==0 :
print("Los vectores son perpendiculares")
else : print("Los vectores no son perpendiculares")
return opcion
s = menu()
print("Elegiste la opción ", s)
122
Programación en Python: Estadística a través de problemas resueltos
print("---------------------------------------")
nombre = input("Nombre del producto: ")
ingredientes_activos = input("Ingredientes activos (separados por \
comas): ").split(",")
fabricante = input("Fabricante del producto: ")
precio = float(input("Precio del producto: "))
cantidad = int(input("Cantidad disponible del producto: "))
productos_fitosanitarios[nuevo_id] = {
"nombre": nombre,
"ingredientes_activos": ingredientes_activos,
"fabricante": fabricante,
"precio": precio,
"cantidad": cantidad
}
print(f"El producto {nombre} ha sido agregado al inventario con éxito.")
productos_fitosanitarios[producto_id]["precio"] = nuevo_precio
print(f"El precio del producto ha sido actualizado con éxito.")
# Menu
while True:
print("1. Ver el inventario")
print("2. Agregar un nuevo producto.")
print("3. Actualizar el precio de un producto existente")
print("4. Salir")
if opcion == "1":
mostrar_inventario()
elif opcion == "2":
agregar_producto()
elif opcion == "3":
actualizar_precio()
elif opcion == "4":
print("Saliendo del programa...")
break
else:
print("Opción inválida, intenta de nuevo")
123
Programación en Python: Estadística a través de problemas resueltos
Recursividad
Un concepto es recursivo si se define en versiones más pequeñas de si mismo.
Por ejemplo:
1, si n = 0
n · (n − 1)! si n > 0
Para estas definiciones no completas, se necesita saber cuándo se acaba la recursividad, es decir,
cuando se detiene el proceso o la también llamada condición de parada.
En el ámbito de la programación una función recursiva es una función que se llama a sí misma
durante su propia ejecución. Se comportan de forma similar a las iteraciones, pero hay que fijar la
condición de parada o se tratará de una función recursiva infinita. Suele utilizarse para dividir una
tarea en subtareas más simples de forma que sea más fácil abordar el problema y solucionarlo.
def cuenta_atras(num):
num -= 1
if num > 0:
print(num)
cuenta_atras(num)
else:
print("Boooooooom!")
print("Fin de la función", num)
cuenta_atras(5)
print("\nResultado",factorial(5))
124
Programación en Python: Estadística a través de problemas resueltos
Construir una función que dados dos números determine el máximo común divisor (MCD).
def lee_natural():
while True:
entrada = input("Escribe un número entero: ")
try:
entrada = int(entrada)
if entrada > 0 :
return entrada
except ValueError:
print ("Entrada es incorrecta: escribe un numero entero positivo")
# ENTRADA DE DATOS
print('Introduzca dos enteros, para los que se calculará el MCD.')
dividendo = lee_natural()
divisor = lee_natural()
# SALIDA
print(f'El MCD de {dividendo} y {divisor} es {mcd}.')
Solución recursiva
def lee_natural():
while True:
entrada = input("Escribe un número entero: ")
try:
entrada = int(entrada)
if entrada > 0:
return entrada
except ValueError:
125
Programación en Python: Estadística a través de problemas resueltos
# ENTRADA DE DATOS
print('Introduzca dos enteros, para los que se calculará el MCD.')
dividendo = lee_natural()
divisor = lee_natural()
# SALIDA
print(f'El MCD de {dividendo} y {divisor} es {mcd}.')
!
n k
P (X = k) = p (1 − p)n−k
k
El coeficiente binomial se puede calcular de forma recursiva usando las siguientes dos propiedades
de los números combinatorios:
! ! !
n n−1 n−1
= +
k k−1 k
! !
n n
= 1, =1
0 n
126
Herramientas para estadística,
probabilidad y tratamiento de datos
Cuando los desafíos se vuelven más complejos Python también ofrece una amplia variedad de
librerías de terceros que facilitan el análisis estadístico avanzado y la manipulación de grandes
volúmenes de información. Además, su activa comunidad de usuarios ha desarrollado y compartido
numerosos paquetes de código abierto que amplían y mejoran las funcionalidades disponibles.
Este capítulo presenta los conceptos básicos de la estadística descriptiva y la probabilidad, con un
enfoque práctico y utilizando Python como herramienta principal. Se explorará cómo las estructuras
de datos de Python, junto con librerías, tanto estándar como construidas por terceros, se integran
con estos principios para construir una base sólida en el análisis de datos y, posteriormente, en la
ciencia de datos.
Una estructura de datos es una forma organizada de guardar y acceder a los datos. Para realizar
análisis estadísticos es esencial contar con estructuras de datos que faciliten su almacenamiento
y manipulación eficiente. Python ofrece de forma estándar diversas de estas estructuras, como
listas, tuplas, conjuntos y diccionarios, cada una con sus propias características y ventajas. Si
bien la selección óptima y el uso eficiente de las estructuras de datos es un tema avanzado, fuera
127
Programación en Python: Estadística a través de problemas resueltos
del alcance de este libro. los ejemplos se centran en casos prácticos que usan todos estos tipos de
datos sin pretender elegir el mejor de ellos en cada caso.
Para realizar los cálculos se pueden utilizar desde las soluciones de la programación clásica que
implican el recorrido de las estructuras de datos, bien sea directamente o utilizando compre-
sión de listas hasta la utilización de las operaciones y métodos ya implementados sobre las
estructuras de datos como el método sum() que es aplicable a listas, tuplas, conjuntos (set)
y diccionarios (dict). En el caso de los diccionarios, si no se especifica lo contrario, sum()
operará sobre las claves. Sin embargo, para sumar los valores del diccionario, se debe utilizar
sum([Link]()). También se utiliza el método sorted(), teniendo en cuenta
que, al aplicarlo a diccionarios, también se ordenarán las claves por defecto.
El enfoque principal de estos ejemplos es la práctica de la programación, utilizando estructuras
de datos y métodos conocidos para realizar cálculos. Por lo tanto, no se priorizará la eficiencia
computacional en este contexto, sino el desarrollo de habilidades de programación. Como primeros
ejemplos se abordan el cálculo de estadísticos de una variable estadística como la media, mediana,
moda, cuartiles, percentiles, deciles, quintiles, varianza y desviación típica.
Media:
sum(datos) / len(datos)
Mediana:
datos_ordenados = sorted(datos)
n = len(datos)
if n % 2 == 1: # Si n es impar
mediana = datos_ordenados[n // 2]
else: # Si n es par
mediana = (datos_ordenados[n // 2 - 1] + datos_ordenados[n // 2]) / 2
Moda:
frecuencia = {}
for num in datos:
frecuencia[num] = [Link](num, 0) + 1
moda = [k for k, v in [Link]() if v == max([Link]())]
128
Programación en Python: Estadística a través de problemas resueltos
Para esta segunda opción, se usa lambda x: x[1] tomando como argumento una tupla x y
devuelve el segundo elemento [1] de la tupla, lo que es la frecuencia de cada valor. De este
modo con max()[1] se encuentra la tupla con mayor frecuencia, y seguidamente devuelve su
valor. En caso de no ser unimodal, la comprensión de listas devuelve la lista con todas las modas
que pudiera tener la distribución de frecuencias.
Cuartiles
datos_ordenados = sorted(datos)
n = len(datos)
q1 = datos_ordenados[n // 4]
q2 = datos_ordenados[n // 2] # Mediana
q3 = datos_ordenados[(3 * n) // 4]
Percentiles
datos_ordenados = sorted(datos)
n = len(datos)
posicion = (percentil / 100) * (n - 1) # el rango en python es de 0 a n-1
i = int(posicion) # parte entera del índice de la posición
decimal = posicion - i # parte decimal de la posición
if i + 1 < n:
return datos_ordenados[i] + decimal * (datos_ordenados[i + 1] - \
datos_ordenados[i])
else:
return datos_ordenados[i]
Desviación estándar
desviacion_estandar = varianza ** 0.5
129
Programación en Python: Estadística a través de problemas resueltos
Escribir un programa que pregunte por una muestra de números, separados por comas, los guarde
en una lista y muestre por pantalla su media y desviación típica.
Primera Solución
texto = input("Introduzca una muestra de números separados por comas: ")
texto = [Link](',')
n = len(texto)
for i in range(n):
texto[i] = int(texto[i])
suma = 0
sum2 = 0
for i in texto:
suma += i
sum2 += i**2
media = suma/n
stdev = (sum2/n-media**2)**(1/2)
frecuencia_numeros = {}
for numero in muestra:
if numero in frecuencia_numeros:
frecuencia_numeros[numero] += 1
else:
frecuencia_numeros[numero] = 1
n = len(muestra)
suma = sum(muestra)
sum2 = sum(numero**2 for numero in muestra)
media = suma / n
stdev = ((sum2 / n) - (media**2))**(1/2)
Construir un programa que defina el espacio muestral para experimentos de lanzamiento, como el
lanzamiento de una moneda o un dado. El programa debe ser generalizable a diferentes tipos de
experimentos y solicitar al usuario la siguiente información:
130
Programación en Python: Estadística a través de problemas resueltos
Una vez que el usuario proporciona esta información, el programa debe generar y mostrar el
espacio muestral completo del experimento.
Para generar el espacio muestral de un experimento tiene sentido utilizar tuplas para definir los
posibles valores de cada suceso.
Solución recursiva
def obtener_entrada():
elementos = int(input("¿Número de elementos en el lanzamiento? "))
opciones = input("¿Opciones separadas por comas? ").split(',')
return elementos, tuple(opciones)
def mostrar_resultado(espacio_muestral):
print("Espacio muestral:")
for resultado in espacio_muestral:
print(resultado)
def main():
elementos, opciones = obtener_entrada()
espacio_muestral = generar_espacio_muestral(opciones, elementos)
mostrar_resultado(espacio_muestral)
if __name__ == "__main__":
main()
En esta versión se inicializa el espacio muestral con una lista que contiene una lista vacía y con
bucles anidados se itera elementos veces para seleccionar cada elemento, se itera sobre las
secuencias existentes, sobre las opciones disponibles y se agrega a la secuencia añadiendo al nuevo
espacio, convirtiéndola en tupla.
def generar_espacio_muestral(opciones, elementos):
espacio_muestral = [[]]
for _ in range(elementos):
nuevo_espacio = []
for secuencia in espacio_muestral:
for opcion in opciones:
nuevo_espacio.append(secuencia + [opcion])
espacio_muestral = nuevo_espacio
return [tuple(secuencia) for secuencia in espacio_muestral]
Se puede aprovechar la potencialidad de los conjuntos para generar los valores de una lista de
ocurrencias.
131
Programación en Python: Estadística a través de problemas resueltos
def contar(lista):
listUnica = valores_unicos(lista)
frec = []
for valor in listUnica:
[Link]([Link](valor))
return listUnica, frec
# Entrada de datos
lista = [18, 32, 20, 18, 30, 35, 24, 33, 32, 30, 20, 20, 24, 20, 30]
# Salida de resultados
print("xi | ni")
print("---+---")
for xi, ni in zip(valores, frecuencias):
print(xi,"|", ni)
A continuación se incluye una solución más eficiente puesto que se mejora el cálculo de frecuencias
en una única iteración. Así, con esta cambio, se hace en una sola pasada sobre la lista y se reduce
la complejidad. Finalmente, se calcula la probabilidad de cada resultado en otro diccionario.
espacio_muestral = {1, 2, 3, 4, 5, 6}
resultados_lanzamiento = [1, 3, 6, 4, 2, 5, 6, 1, 3, 4, 5, 2, 6, 1, 3, 2, 4, 5, 6, 1]
132
Programación en Python: Estadística a través de problemas resueltos
total_lanzamientos = len(resultados_lanzamiento)
probabilidad_resultados = {num: frecuencia[num] / total_lanzamientos for num in espacio_muestral}
Este problema se puede hacer más eficiente utilizando tuplas en lugar de conjuntos y diccionarios.
Las tuplas mejoran el rendimiento en algunos casos, especialmente cuando el acceso a los datos es
constante y no se necesita modificar la estructura. Así pues, el código optimizado siguiente hace
más rápido y ocupa menos memoria, sobre todo si el número de lanzamientos es muy grande.
Además, en la versión final siguiente se utiliza una validación para evitar errores en caso de valores
inesperados en los resultados de los lanzamientos.
espacio_muestral = (1, 2, 3, 4, 5, 6)
resultados_lanzamiento = (1, 3, 6, 4, 2, 5, 6, 1, 3, 4, 5, 2, 6, 1, 3, 2, 4, 5, 6, 1)
total_lanzamientos = len(resultados_lanzamiento)
probabilidad_resultados = tuple(f / total_lanzamientos for f in frecuencia)
133
Programación en Python: Estadística a través de problemas resueltos
Con las soluciones anteriores se ha querido mostrar al lector una exploración de diversos tipos de
datos (como listas, tuplas y conjuntos), así como distintas estrategias para calcular frecuencias
de manera eficiente. Además, se ha comentado cómo optimizar el rendimiento de los cálculos
utilizando estructuras de datos adecuadas y evitando operaciones innecesarias, así como el uso
repetido de .count().
Estas prácticas no solo mejoran la eficiencia del código, sino que también proporcionan una
comprensión más profunda de las estructuras de datos y su aplicación en problemas reales.
Además de los ejercicios que se han construido anteriormente utilizando recursividad, se puede
134
Programación en Python: Estadística a través de problemas resueltos
utilizar los módulos math y el módulo itertools tanto para el cálculo del número de ellas como
para generarlas. Previo al desarrollo de algunos ejercicios resueltos se indica lo siguiente para el
uso de itertools.
Ejercicio resuelto. Dado un grupo de tres letras A, B, C ¿de cuántas maneras se pueden ordenar?
Como siempre se tienen dos formas para hacerlo, directamente codificando o bien utilizando un
módulo ya desarrollado, en este caso itertools.
# Lista de elementos
elementos = ['A', 'B', 'C']
Solución alternativa:
from itertools import permutations
135
Programación en Python: Estadística a través de problemas resueltos
# Cálculo
resultado = permutaciones_con_repeticion(n, repeticiones)
print("Número de permutaciones con repetición:", resultado)
Una alternativa aquí es utilizar math para realizar el conteo, con lo que se omitiría la función
factorial(n) y bastaría sólo llamar a [Link]().
import math
n = 4
repeticiones = [2, 1, 1]
Si además del conteo, se quiere calcular cuáles son éstas, se recurre a la libreria itertools.
from itertools import permutations # Permutaciones con repetición
elementos = "CASA"
permutaciones = set(permutations(elementos))
print("Permutaciones únicas:")
for p in permutaciones:
print("".join(p))
n, k = 5, 2 # Ejemplo
print(f"C({n}, {k}) =", combinaciones(n, k)) # Debe imprimir C(5,2) = 10
Solución alternativa:
from math import comb
136
Programación en Python: Estadística a través de problemas resueltos
n, k = 5, 2
print(f"C({n}, {k}) =", comb(n, k)) # C(5,2) = 10
Si esas preguntas tienen tres opciones de respuesta, ¿cuántas posibles formas de mostrar las
opciones de respuesta hay si se baraja el orden de presentación de cada una de las opciones?
from itertools import permutations
# Opciones de respuestas
respuestas = ["A", "B", "C"]
# Filtrar las combinaciones cuya suma sea mayor que la suma proporcionada
comb_validas = [combinacion for combinacion in combinaciones \
if sum(combinacion) > suma_superar]
print("combinaciones validas: \n", comb_validas)
# Calcular la probabilidad
probabilidad = comb_validas_count / total_combinaciones
# Mostrar resultados
print(f"Total de combinaciones posibles: {total_combinaciones}")
print(f"Combinaciones cuya suma es mayor que {suma_superar}: {comb_validas_count}")
print(f"Probabilidad de que la suma sea mayor que {suma_superar}:{probabilidad:.4f}\n")
def main():
while True:
try:
num_dados = int(input("Deme el número de dados a lanzar: "))
suma_superar = int(input("Deme la suma mínima a superar: "))
except ValueError:
print("Por favor, introduzca números enteros válidos.")
continue
137
Programación en Python: Estadística a través de problemas resueltos
En el experimento aleatorio de lanzar un dado, calcular las probabilidades de ser par, impar y la
probabilidad de la intersección de sucesos.
138
Programación en Python: Estadística a través de problemas resueltos
Librería statistics
El paquete statistics es un módulo útil, aunque poco conocido, en las librerías estándar de
Python. Proporciona funciones que permiten calcular casi todos los valores estadísticos, como la
media, la covarianza, etc. Para cálculos estadísticos sencillos, en lugar de instalar una biblioteca
externa como NumPy, se puede usar este módulo integrado.
Se incluye un script de muestra de las funciones incorporadas en esta librería:
import random
import statistics as st
# Moda y cuartiles**
print("Cuartiles:", [Link](numOrd))
num = [1, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5]
print("Moda:", [Link](num))
print("Multimoda:", [Link](num))
Ejercicio resuelto. Un agricultor, que en cada cosecha registra la cantidad de fertilizante (en
kg/ha) aplicada a diferentes parcelas de un cultivo y el rendimiento obtenido (en toneladas/ha) en
cada una de ellas, necesita un programa que le permita analizar la relación entre estas variables y
predecir el rendimiento en futuras cosechas.
139
Programación en Python: Estadística a través de problemas resueltos
# 2. Regresión lineal
pendiente, interseccion = st.linear_regression(datos['fertilizante_kg_ha'], \
datos['rendimiento_ton_ha'])
140
Programación en Python: Estadística a través de problemas resueltos
y rendimiento."
elif 0.30 <= correlacion < 0.50:
return "débil y positiva, indicando una relación poco clara entre fertilizante y \
rendimiento."
elif -0.30 < correlacion < 0.30:
return "muy débil o nula, indicando que la relación entre fertilizante y rendimiento \
es prácticamente inexistente."
elif -0.50 <= correlacion <= -0.30:
return "débil y negativa, indicando una ligera tendencia a que el rendimiento \
disminuya al aumentar el fertilizante."
elif -0.70 <= correlacion < -0.50:
return "moderada y negativa, indicando una tendencia notable a que el rendimiento \
disminuya al aumentar el fertilizante."
elif -0.85 <= correlacion < -0.70:
return "moderadamente fuerte y negativa, indicando una tendencia significativa a que \
el rendimiento disminuya al aumentar el fertilizante."
elif -0.95 <= correlacion < -0.85:
return "fuerte y negativa, indicando una fuerte tendencia a que el rendimiento \
disminuya al aumentar el fertilizante."
elif corre lacion <= -0.95:
return "muy fuerte y negativa, indicando una relación casi perfecta inversa entre \
fertilizante y rendimiento."
interpretacion_correlacion = interpretar_correlacion(correlacion)
Para completar este apartado sería conveniente utilizar un gráfico que representara los datos,
es aquí donde cabe mostrar un diagrama de punto y la recta de regresión, pero aún no se han
detallado las librerías que contienen elementos gráficos. Se verá más tarde.
Ejercicio resuelto. Con este ejercicio se analiza la relación entre el período orbital (en días) y la
distancia al Sol (en millones de kilómetros) de los planetas del sistema solar.
Primero, se ha de verificar si existe una relación monótona perfecta entre ambas variables utilizando
la correlación por rangos. Luego, se observa que, aunque la correlación lineal es muy alta, no es
perfecta. Finalmente, se aplica la tercera ley de Kepler, que establece que el cuadrado del período
orbital es proporcional al cubo de la distancia al Sol, demostrando que esta relación es lineal
cuando se ajustan los valores adecuadamente.
141
Programación en Python: Estadística a través de problemas resueltos
Planeta
enano Período Orbital(días) Distancia real al Sol (millones de km)
Plutón 90560 5906
Eris 204199 10152
Makemake 111845 6796
Haumea 103410 6450
Ceres 1680 414
import statistics as st
if proportional:
pendiente = sum_xy / sum_x2
interseccion = 0
else:
pendiente = (n*sum_xy - sum_x*sum_y) / (n*sum_x2 - sum_x*sum_x)
interseccion = (sum_y - pendiente * sum_x) / n
return pendiente, interseccion
# Predecir las distancias al sol para los planetas enanos usando la tercera
# ley de Kepler. Se usa una función lambda y round en lugar de [Link]
distancia_al_sol_predicciones = [round((pendiente * (p * p))**(1/3)) \
for p in periodo_orbital_planetas_enanos]
142
Programación en Python: Estadística a través de problemas resueltos
Librerías de terceros
Los principales paquetes de terceros que ofrece Python para trabajar con estadística y probabilidad
son los siguientes, algunas se han convertido en un estándar de facto como NumPy y Pandas:
NumPy, Numerical Python, es uno de los paquetes más importantes para la computación
numérica en Python. Siendo las matrices numéricas su principal aportación.
[Link], este submódulo del paquete científico Scipy una librería para el análisis
estadístico. Proporciona herramientas para la estimación de parámetros, la generación de
muestras aleatorias, las pruebas de hipótesis y la modelización de distribuciones estadísticas.
Statsmodels, es una librería para el modelado estadístico. Proporciona funciones para
la estimación de modelos lineales, modelos de series de tiempo, modelos de regresión y
modelos de supervivencia. También tiene funciones para la evaluación del ajuste del modelo
y la inferencia estadística.
Pandas, centrada en el análisis de datos su principal aportación son los tipos de datos
Series y DataFrame. Adopta partes significativas del estilo basada en matrices de NumPy.
Posee algunas funciones muy útiles para realizar estadística descriptiva sobre datos. También
tiene funciones para la lectura y escritura de datos en diferentes formatos, como CSV, Excel
y bases de datos SQL.
Matplotlib, es la librería que se centra en las visualizaciones y gráficos que junto a
Seaborn proporciona opciones avanzadas para la creación de gráficos estadísticos complejos,
como diagramas de violín, gráficos de regresión y mapas de calor.
143
Programación en Python: Estadística a través de problemas resueltos
Si no están instalados en necesario utilizar desde la consola pip (el gestor de paquetes que permite
instalar, actualizar y administrar librerías y paquetes de terceros):
pip install <nombre_librería>
Cada una de estas librerías tiene ventajas y limitaciones, por lo que la elección dependerá de las
necesidades específicas del proyecto. En términos generales, NumPy es la base para el análisis
numérico en Python, mientras que Pandas y Matplotlib son fundamentales para la manipulación
y visualización de datos. Por otro lado, [Link] y Statsmodels resultan especialmente
144
Programación en Python: Estadística a través de problemas resueltos
útiles para el análisis estadístico y el modelado, y Seaborn ofrece herramientas avanzadas para
representar datos estadísticos de manera más intuitiva.
Especificamente NumPy y Pandas, son librerías que ofrecen mejoras significativas en términos
de eficiencia, facilidad de manipulación y escalabilidad con los vectores que otros tipos de datos
incorporados en Python como las listas.
Una de las grandes aportaciones Numpy es la la definición de un tipo de datos más eficiente que las
listas para las grandes cantidades de datos. De forma general, un vector o array es una estructura
de datos ordenada de elementos homogéneos cuyo orden viene definido por su posición (índice) no
por su contenido. Los elementos se distribuyen en filas (1D), filas y columnas (2D), o incluso más
dimensiones. Estas dimensiones son los índices que permiten acceder por posición a los elementos
guardados en esta estructura de datos. Si bien esto ya se puede hacer con las listas Python
se apoya en diversas librerías especializadas para el manejo de colecciones indexadas grandes y
complejas. Especificamente NumPy y Pandas, son librerías que ofrecen mejoras significativas en
términos de eficiencia, facilidad de manipulación y escalabilidad de los vectores que los tipos de
datos incorporados en Python como las listas.
Función Descripción
[Link]() Calcula la media aritmética de los elementos de un array
[Link]() Calcula la mediana de los elementos de un array
145
Programación en Python: Estadística a través de problemas resueltos
Función Descripción
[Link]() Calcula la desviación estándar de los elementos de un array
[Link]() Calcula la varianza de los elementos de un array
[Link]() Encuentra el valor máximo en un array
[Link]() Encuentra el valor mínimo en un array
[Link]() Calcula el percentil especificado de los elementos de un array
[Link]() Calcula un histograma de los elementos de un array
[Link]() Genera muestras aleatorias de una distribución normal
[Link]() Genera muestras aleatorias de una distribución binomial
[Link]() Genera muestras aleatorias de una distribución de Poisson
[Link]()Genera muestras aleatorias de una distribución exponencial
[Link]() Genera muestras aleatorias de una distribución uniforme
[Link](n) Devuelve un array con números aleatorios desde 0 hasta n-1
[Link](n) Devuelve un array de n números aleatorios desde 0.0 hasta 1.0
(exlcluido)
[Link]() Genera una muestra aleatoria de un array
[Link]() Calcula la matriz de coeficientes de correlación de Pearson
[Link]() Calcula la matriz de covarianza de un array
[Link]() Ajusta una función polinomial a un conjunto de datos utilizando el
método de mínimos cuadrados
[Link]() Evalúa una función polinomial en un conjunto de valores
[Link]() Calcula el gradiente de un array multidimensional
[Link]() Calcula la transformada de Fourier discreta de un array
[Link]() Calcula la transformada inversa de Fourier discreta de un array
Numpy da soporte para álgebra lineal ofreciendo un conjunto completo de herramientas para
realizar operaciones de álgebra lineal, como la multiplicación de matrices, la resolución de sis-
temas de ecuaciones lineales, el cálculo de autovalores y autovectores, etc. También facilita la
manipulación de datos científicos, incluyendo la lectura y escritura de archivos en diferentes
formatos, la gestión de datos faltantes y la realización de transformaciones de datos.
Otra de las grandes fortalezas de esta librería es que es la base para otras , como Pandas
que e utiliza NumPy para la representación y manipulación de datos en formato tablas de
datos, SciPy que extiende NumPy con funciones adicionales para optimización, integración,
ecuaciones diferenciales. TensorFlow y Scikit-learn que utilizan NumPy para la representación y
procesamiento de datos en tareas de aprendizaje automático e inteligencia artificial, finalmente
Matplotlib: se combina con la librería Numpy para declarar los datos de entrada y generar
visualizaciones de datos.
En el núcleo de NumPy está el ndarray, donde nd es por n-dimensional. Un ndarray es una
146
Programación en Python: Estadística a través de problemas resueltos
estructura de datos multidimensional de elementos del mismo tipo que permite representar datos
de forma natural como matrices y vectores, estructuras comunes en problemas estadísticos y
probabilísticos. Son más rápidos y consumen menos memoria que las listas de Python pudiendose
realizar operaciones matemáticas en vectores completos sin necesidad de bucles que los recorran,
lo que mejora el rendimiento.
Un ejemplo básico muestra las operaciones sobre vectores. En concreto se genera un vector
aleatorio de 2x3, imprime el array original y luego lo imprime para después de sumarlo a sí mismo
y que empieza siempre por la importación de la librería.. Esto demuestra la manipulación básica y
las operaciones numéricas dentro de NumPy, una habilidad fundamental para la programación y el
análisis estadístico en Python.
import numpy as np
datos = [Link](2, 3)
print(datos)
print("\n",datos + datos)
Hay muchas otras funciones para manipular un ndarray y realizar cálculos matemáticos complejos
en Python. A continuación se muestran algunos ejemplos:
import numpy as np
147
Programación en Python: Estadística a través de problemas resueltos
# Multiplicación de matrices mA y mB
mA = [[1,2], [5,3]]
mB = [[4,5], [2,3]]
matrizC = [Link](mB)
Otras funciones aplicables son ayuda a conocer la información sobre la estructura de datos:
import numpy as np
x = [Link]([1, 2, 3, 4, 5])
print([Link]) # dimensión: 1
print([Link]) # tamaño total del array: 5
print([Link]) # forma: (5,)
print([Link]) # tipo de sus elementos: int64
Es posible crear un vector con valores de distintos tipos de datos al contrario que en otros lenguajes.
Lo que realmente se produce (de forma implícita) se convierte un tipo de datos a otro sin tener
en cuenta la comprobación de tipos, hay que recordar que esa escrita en C, que tiene tipificación
dinámica.
¿ndarray o list? El uso de ndarray frente a una lista está justificado por cuestiones de
rendimiento. En el siguiente ejemplo que suma 10 millones de valores enteros se clarifica el tiempo
de cómputo de cálculo para ambas estructuras.
array_as_list = list(range(int(10e6)))
%timeit sum(array_as_list) # Captura el tiempo
En este caso el resultado es: 91 ms ± 19.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops
each)
array_as_ndarray = [Link](array_as_list)
%timeit array_as_ndarray.sum() # Captura el tiempo
Cuando se usa NumPy es mas rápido: 7.64 ms ± 436 µs per loop (mean ± std. dev. of 7 runs,
100 loops each)
En cualquier caso, existe la posibilidad de convertir a lista cualquier ndarray mediante el método
tolist().
148
Programación en Python: Estadística a través de problemas resueltos
Ejercicio resuelto. Una finca produce tres tipos de productos: manzanas, naranjas y plátanos.
La producción de cada producto se mide en kilogramos (kg). Se tienen los siguientes datos:
Producción diaria:
• Día 1: 100 kg de manzanas, 50 kg de naranjas, 20 kg de plátanos
• Día 2: 120 kg de manzanas, 60 kg de naranjas, 30 kg de plátanos
• Día 3: 90 kg de manzanas, 40 kg de naranjas, 25 kg de plátanos
Precio por kilogramo:
• Manzanas: 2€/kg
• Naranjas: 1.5€/kg
• Plátanos: 0.8€/kg
Calcular el ingreso total de la granja para cada día y el ingreso total para los tres días.
import numpy as np
149
Programación en Python: Estadística a través de problemas resueltos
import numpy as np
Si ahora se desea calcular la tabla de frecuencias, se recurre a [Link]. Para nuestro ejemplo
bastará con encontrar los valores únicos que aparecen y devolver el conteo de éstos para calcular
su frecuencia.
devuelve para el array pasado como primer algumento los valores observados únicos, sus corre-
spondientes índices, el valor inverso de éstos, y el conteo de los valores observados únicos. Si se
omite el argumento, se toma el valor False.
Valores Únicos: [1 2 3 4 5 6]
Frecuencia de Valores Únicos: [155 168 174 160 189 154]
Valor | fi
==============
1 | 0.155
2 | 0.168
3 | 0.174
4 | 0.16
5 | 0.189
6 | 0.154
Como se acaba de decir NumPy es una librería fundamental para el cálculo numérico en Python,
eficiente para almacenar y manipular datos numéricos multidimensionales teniendo que ser todos
los elementos del mismo tipo de datos. Éstos no tienen etiquetas para los datos (solo índices
numéricos) y es aquí, en los análisis de datos donde se necesitan almacenar datos de diferentes
tipos (números, texto, fechas, etc.) donde es útil tener etiquetas (nombres) para las columnas
o variables, en lugar de solo índices numéricos. NumPy no ofrece estas características de forma
directa y es lo que provoca el nacimiento de la siguiente librería Pandas.
150
Programación en Python: Estadística a través de problemas resueltos
Así como los ndarray son la base de NumPy, en Pandas existen dos estructuras de datos
principales: Series y DataFrame. Una Series es una estructura unidimensional similar a un
vector de NumPy, pero con etiquetas para cada dato, mientras que un DataFrame es una
estructura bidimensional similar a una tabla, con filas y columnas etiquetadas, ideal para el análisis
de datos tabulares.
# Crear una Series
s = [Link]([1, 2, 3, 4])
# Crear un DataFrame
df = [Link]({'A': [1, 2, 3], 'B': [4, 5, 6]})
Pandas Series:
Nombre [Planta A, Planta B, Planta C]
Altura (cm) [15.2, 12.8, 17.5]
Fecha DatetimeIndex(['2023-10-26', '2023-10-27', '20...
Germinó [True, False, True]
dtype: object
Como se observa el vector de NumPy datos_planta almacena todos los datos como cadenas de
texto, perdiendo la información de tipos. Una serie serie_planta permite almacenar datos de
diferentes tipos (texto, números, fechas, booleanos) y proporciona etiquetas para cada variable.
151
Programación en Python: Estadística a través de problemas resueltos
Pandas esta optimizada para la lectura/escritura en archivos de multiples formatos: CSV, Excel,
JSON, SQL, HDF5, entre otros con funciones como pd.read_csv() y df.to_csv(). Se
complementa con NumPy, Matplotlib, Seaborn y Scikit-learn para visualización y análisis de
datos. Esta especialmente pensada para filtrar, ordenar, agrupar, fusionar y transformar grandes
volúmenes de datos de manera sencilla, con operaciones como [Link](), [Link](),
df.pivot_table() y métodos como [Link]() y [Link]() facilitan el manejo de
valores nulos.
Tipo Series
Una Serie en Pandas suele ser homogénea, es decir, sus elementos suelen ser del mismo tipo,
aunque Pandas puede manejar tipos mixtos si es necesario. Su tamaño es flexible, en el sentido de
que los datos pueden modificarse y se pueden generar nuevas series con más o menos elementos.
La forma más fácil de pensar en los objetos de la serie Pandas es como un contenedor para dos
matrices Numpy, una para el índice o etiquetas y otra para los datos. Las matrices Numpy ya
tienen indexación de enteros al igual que las listas regulares de Python.
Para crear una serie se utiliza [Link] y puede tener diversos argumentos como los representan
los datos, los indices, o el nombre. Los valores por defecto para los indices se usan los enteros del
0 al n-1, donde n es el tamaño de la serie
data: Los datos que se van a almacenar en la serie. Pueden ser listas, arrays de NumPy,
diccionarios, etc.
index: Las etiquetas para el índice. Si no se especifica, Pandas crea un índice numérico
por defecto (0, 1, 2, . . . ).
name: Un nombre para la serie (opcional, pero útil para la organización).
dtype: El tipo de dato de los elementos de la serie.
import pandas as pd
# Método constructor de series a partir de una lista, el indice de 0 a 5
x = [Link](data=[1, 2, 30, 0, 15, 6],
dtype='int64', # Especificando el tipo de dato
name='Números') # Añadiendo un nombre a la serie
152
Programación en Python: Estadística a través de problemas resueltos
El acceso es similar a las colecciones de Python pero se puede acceder usando tanto la etiqueta
como el indice. En ambos casos es puede hacer indexación (para un elemento) o segmentación
(una lista de posiciones).
Utilizando la ubicación numérica de los elementos dentro de la serie, similar a cualquier indexación
sobre estructuras de datos Python
El acceso por índice se realiza utilizando las etiquetas que se han asignado al índice de la serie.
Esto permite acceder a los elementos por su nombre o etiqueta en lugar de su posición numérica.
153
Programación en Python: Estadística a través de problemas resueltos
print(resultado)
En caso de series con longitud considerable, se tienen los métodos head y tail para manejar las
posiciones iniciales o finales de la serie. Permiten acceder a un trozo de los datos para visualizar
como son.
[Link](3) # tres primeros elementos
Existen varias propiedades o métodos para ver las características de una serie.
[Link]: Devuelve el número de elementos de la serie s.
[Link]: Devuelve una lista con los nombres de las filas de s.
[Link]: Devuelve el tipo de datos de los elementos de s.
[Link] #valores
[Link][1:3]
[Link] #índice de la serie
[Link] #tipo de la serie
[Link] #nombre de la serie
[Link] #número de registros de la serie
154
Programación en Python: Estadística a través de problemas resueltos
O bien
# Ordenación por índice
x.sort_index()
También es posible aplicar una función a cada elemento de la serie mediante el siguiente método:
[Link](f): Devuelve una serie con el resultado de aplicar la función f a cada uno de los
elementos de la serie s.
import pandas as pd
from math import log
Función Descripción
[Link]() Devuelve el número de elementos que no son nulos ni NaN en la serie s
[Link]() Devuelve la suma de los datos de la serie s cuando los datos son de un tipo
numérico, o la concatenación de ellos cuando son del tipo cadena str
[Link]() Devuelve una serie con la suma acumulada de los datos de la serie s cuando
los datos son de un tipo numérico
s.value_counts()Devuelve una serie con la frecuencia de cada valor de la serie s
[Link]() Devuelve el menor de los datos de la serie
[Link]() Devuelve el mayor de los datos de la serie
[Link]() Devuelve la posición/índice del valor mínimo
155
Programación en Python: Estadística a través de problemas resueltos
Función Descripción
[Link]() Devuelve la posición/índice del valor máximo de una serie
[Link]() Devuelve la etiqueta/índice del valor mínimo de una serie
[Link]() Devuelve la etiqueta/índice del valor máximo de una serie
[Link](n) Devuelve los n valores menores de una serie
[Link](n) Devuelve los n valores mayores de una serie
[Link]() Devuelve la media de los datos de la serie cuando los datos son de un tipo
numérico
[Link]() Devuelve la cuasi-varianza de los datos de la serie cuando los datos son de
un tipo numérico
[Link]() Devuelve la cuasi-desviación típica de los datos de la serie cuando los datos
son de un tipo numérico
[Link]() Devuelve una serie con un resumen que incluye el número de datos, su
suma, el mínimo, el máximo, la media, la desviación típica y los cuartiles
Se mostrará un resumen estadístico descriptivo y se calcularán los elementos con una producción
de más de 4 toneladas.
import pandas as pd
produccion = [3.2, 4.5, 5.1, 2.8, 3.9, 4.1, 2.5, 4.7, 4.2, 4.9, 3.6, 4.4, 3.8,
4.3, 3.7, 2.9, 4.0, 4.5, 3.6, 3.2, 2.7, 3.3, 3.9, 3.5, 3.0, 3.7, 4.0,
4.2, 4.6, 3.5, 4.1, 2.8, 4.4, 4.8, 4.6, 4.2, 4.1, 4.2, 4.5, 4.7, 4.4,
4.0, 4.3, 3.8, 3.5, 3.6, 4.5, 4.1, 3.2, 3.9]
156
Programación en Python: Estadística a través de problemas resueltos
seleccionar solo los días en los que la temperatura máxima superó los 30 grados Celsius. De igual
forma, obtener la tabla de distribución de frecuencias como composición de Series para formar las
columnas de la tabla.
Las temperaturas registradas son: 28, 30, 33, 29, 31, 35, 26, 27, 30, 32, 34, 28,
29, 30, 31, 32
import pandas as pd
temperaturas = [Link]([28, 30, 33, 29, 31, 35, 26, 27, 30, 32, 34, 28, 29, 30, 31, 32])
# Seleccionamos los días en los que la temperatura máxima superó los 30 grados Celsius
dias_calurosos = temperaturas[temperaturas > 30]
print(dias_calurosos)
Tipo DataFrame
Un DataFrame es una estructura de datos bidimensional etiquetada con filas y columnas (columnas
de tipos potencialmente diferentes como enteros, cadenas, float, None, objetos Python, etc.). Su
comportamiento y operaciones son parecidas a las series pero en dos dimensiones.
Se pueden crear DataFrame a partir de series, ndarrays, listas y diccionarios. Se puede relacionar
a una hoja de cálculo o una tabla SQL, o un diccionario de objetos Series.
Es el objeto Pandas más utilizado, cada columna es de tipo Series, por lo que los datos de cada
columna son del mismo tipo. Se puede considerar como una concatenación de series, donde cada
una tiene a su vez un índice (columns). En los DataFrame se puede especificar tanto el index
(el nombre de las filas) como columns (el nombre de las columnas)
# Crear un DataFrame
df = [Link]({'agricultor':['Bernardo','Sonia','Tomás'], 'edad':[20,30,40],
'cultivo_principal':['tomate', 'calabacín', 'sandía']},
index = ['agri1', 'agri2', 'agri3']
)
# lista de columnas
[Link]
# lista de índices
[Link]
157
Programación en Python: Estadística a través de problemas resueltos
Se puede asignar a una columna todos los valores iguales, por ejemplo, todas las personas con la
edad a 10.
[Link] = 10
Función Descripción
[Link]() Devuelve información (número de filas, número de columnas, índices, tipo de las
columnas y memoria usado) sobre df.
[Link] Devuelve una tupla con el número de filas y columnas de df.
[Link] Devuelve el número de elementos del DataFrame.
[Link] Devuelve una lista con los nombres de las columnas de df.
[Link] Devuelve una lista con los nombres de las filas de df.
[Link] Devuelve una serie con los tipos de datos de las columnas de df.
[Link](n) Devuelve las n primeras filas de df.
[Link](n) Devuelve las n últimas filas de df.
Al igual que para las series, hay métodos que permiten resumir la información de un DataFrame:
158
Programación en Python: Estadística a través de problemas resueltos
Función Descripción
[Link]() Devuelve una serie con el número de elementos que no son nulos ni NaN en
cada columna de df.
[Link]() Devuelve una serie con la suma de los datos de las columnas de df cuando
los datos son de un tipo numérico, o la concatenación de ellos cuando son del
tipo cadena str.
[Link]() Devuelve un DataFrame con la suma acumulada de los datos de las columnas
de df cuando los datos son de un tipo numérico.
[Link]() Devuelve una serie con los menores de los datos de las columnas de df.
[Link]() Devuelve una serie con los mayores de los datos de las columnas de df.
[Link]() Devuelve una serie con las medias de los datos de las columnas numéricas de
df.
[Link]() Devuelve una serie con las varianzas de los datos de las columnas numéricas
de df
[Link]() Devuelve una serie con las desviaciones típicas de los datos de las columnas
numéricas de df
[Link]() Devuelve un DataFrame con las covarianzas de los datos de las columnas
numéricas de df.
[Link]() Devuelve un DataFrame con los coeficientes de correlación de Pearson de los
datos de las columnas numéricas de df.
[Link] Devuelve un DataFrame con un resumen estadístico de las columnas de df
(include = del tipo tipo. Para los datos numéricos se calcula la media, la desviación
tipo) típica, el mínimo, el máximo y los cuartiles. Para los datos no numéricos
(object) se calcula el número de valores, el número de valores distintos, la
moda y su frecuencia. Sin tipo solo se consideran las columnas numéricas
159
Programación en Python: Estadística a través de problemas resueltos
• Desviación estándar.
• Calificación más alta.
• Calificación más baja.
Mostrar resultados: Imprime los resultados de los cálculos de forma clara y organizada.
Utilizar describe(): Utiliza esta función de Pandas para obtener un resumen estadístico de
las calificaciones en ambas asignaturas. Asegúrate de que los resultados se muestren con
tres decimales de precisión.
Análisis de correlación: Calcula e imprime el coeficiente de correlación entre las calificaciones
de Estadística y Probabilidad. Interpreta el resultado brevemente.
import pandas as pd
import numpy as np
160
Programación en Python: Estadística a través de problemas resueltos
Matplotlib está construido sobre el paquete NumPy y trabaja de forma natural con los vectores
y matrices (arrays) asociados a él. Dado que en los ejemplos anteriores se han presentado datos
numéricos sin su correspondiente representación gráfica, es conveniente introducir ahora esta
herramienta para completar dichos ejemplos y mejorar la comprensión de los resultados.
Por el momento, se trabajará con listas nativas de Python, las cuales serán internamente
transformadas a arrays de NumPy en las funciones de Matplotlib. Este proceso es transparente
para el usuario, pero se debe tener en cuenta que las listas utilizadas deben estar formadas por
valores del mismo tipo de datos.
Dentro del paquete matplotlib destaca el módulo pyplot. Este módulo oculta muchas de las
funcionalidades de bajo nivel de la biblioteca, permitiendo el uso de sencillas funciones para los
elementos gráficos más habituales, tales como creación de figuras, trazado de líneas, visualización
de imágenes, inserción de texto, etc. El módulo pyplot emula el entorno de programación gráfica
de MATLAB, herramienta software de pago muy popular en universidades y empresas.
Dado que en los ejemplos resueltos anteriores no se han incluido representaciones gráficas de
los datos, a continuación se introducirá el uso de la función .plot(), que permite visualizar de
manera efectiva las listas de valores proporcionadas. Esta incorporación facilitará la interpretación
de los resultados y reforzará el vínculo entre los conceptos teóricos y su aplicación práctica.
Los gráficos de líneas son útiles para visualizar tendencias o patrones en datos secuenciales, como
series temporales. En estadística, se utilizan para representar la evolución de una variable a lo
largo del tiempo.
import [Link] as plt
# Mostrar el gráfico
[Link]()
Se ha de invocar la función .show(), que es la que de forma efectiva muestra la figura 15.
161
Programación en Python: Estadística a través de problemas resueltos
En el caso de ser los datos numéricos se utilizan vectores para declarar los datos (figura 16):
# Crear el gráfico
[Link](fertilizante, rendimiento, marker='o', linestyle='-')
# Personalizar el gráfico
[Link]("Fertilizante nitrogenado (kg/ha)")
[Link]("Rendimiento del cultivo (ton/ha)")
[Link]("Relación entre fertilizante y rendimiento")
[Link](True, alpha=0.2)
162
Programación en Python: Estadística a través de problemas resueltos
También se pueden representar funciones matemáticas e incluso dos funciones en una misma
gráfica: parábola y su recta tangente evaluada en un punto (figura 17). En el siguiente código se
han añadido los ejes en la representación. Esto se hace con axhline y axvline teniendo en
cuenta los distintos argumentos opcionales de palabra clave de la forma kwarg=valor:
import numpy as np
import [Link] as plt
def derivada_parabola(x):
return 2*x # Derivada de x² - 3
# 3. Generar valores de X
x = [Link](-10, 10, 100)
# 5. Crear el gráfico
[Link](x, y_parabola, label='Parábola: y = x² - 3', color='red')
[Link](x, y_recta, label=f'Tangente en x = {x0}', color='blue')
[Link](x0, y0, marker='o', markersize=8, color='green',label=f'Punto de tangencia ({x0}, {y0})')
# 7. Personalizar el gráfico
[Link]("X")
[Link]("Y")
[Link]("Parábola y su recta tangente")
[Link]()
[Link](True, alpha=0.2)
# 8. Mostrar el gráfico
[Link]()
163
Programación en Python: Estadística a través de problemas resueltos
Si lo que se quiere es un gráfico de puntos donde se fijan etiquetas para los ejes y un título para
el gráfico (figura 18).
temperatura = [ 14.2, 16.4, 11.9, 15.2, 18.5, 22.1, 19.4, 25.1,
23.4, 18.1, 22.6, 17.2]
ventas = [ 215, 325, 185, 332, 406, 522, 412, 614, 544, 421, 445, 408]
[Link](temperatura, ventas)
[Link]('Temperatura(oC)')
[Link]('Ventas')
[Link]('Temperatura vs Ventas de helados')
[Link]()
164
Programación en Python: Estadística a través de problemas resueltos
Gráfico de barras
Para generar un gráfico de barras se usará bar() y para personalizarlo se detallan algunas
cuestiones a continuación tal como se muestra en la figura 19.
[Link](), [Link](), [Link](): Estas funciones agregan etiquetas al
eje x, al eje y y al título del gráfico, respectivamente.
[Link](x, etiquetas): Reemplaza las marcas predeterminadas del eje x (números)
con las etiquetas de categoría de la lista etiquetas.
Con el bucle for se agrega el valor numérico de cada barra encima de la barra para mayor
claridad.
[Link](axis='y', linestyle='--', alpha=0.5): Agrega una cuadrícula hori-
zontal al gráfico para facilitar la lectura de los valores, con un estilo de línea discontinua y
transparencia media.
165
Programación en Python: Estadística a través de problemas resueltos
# Mostrar el gráfico
[Link]()
Gráfico de sectores
Para el gráfico de sectores se utiliza pie() y se detallan los siguientes argumentos (ver figura 20):
autopct=' %1.1f % %': Se encarga de mostrar los porcentajes en cada sector del círculo.
Para formatear los porcentajes:
• %: Indica el inicio de un especificador de formato.
• 1.1f: Define el formato del número:
◦ 1: Ancho mínimo de un carácter.
◦ .1: Precisión de un decimal.
◦ f: Tipo de dato: número punto flotante.
• % %: Escapa el símbolo de porcentaje ( %) para que se muestre en el gráfico
startangle=90: Este argumento controla la rotación del círculo. Indica que se rotará 90
grados en sentido antihorario. Esto significa que el primer sector (primer valor en datos)
comenzará en la posición de las 12 en punto (en la parte superior del gráfico).
166
Programación en Python: Estadística a través de problemas resueltos
# Mostrar el gráfico
[Link]()
Histograma
El histograma es una herramienta visual poderosa para explorar la distribución de tus datos.
La elección del número de intervalos (bins) es crucial para una correcta interpretación. A
continuación se muestra un ejemplo sobre cómo considerar las reglas teóricas de Sturges y del
criterio de la raiz como punto de partida, ajustando el número de intervalos (bins) según la
naturaleza de los datos y del objetivo del análisis.
En el código se crea una única figura con dos subgráficos (dos histogramas) en los que se compara
√
el número de intervalos k a considerar, bien usando el criterio de la raiz de la muestra k = n,
bien usando la regla de Sturges 1 + log2 (n).
Se usará figura, ejes = [Link](filas,columnas, figsize=(ancho,alto))
para crear una única figura con dos subgráficos pudiendo ajustar el tamaño en pulgadas.
import [Link] as plt
import numpy as np
167
Programación en Python: Estadística a través de problemas resueltos
En este caso, el criterio de la regla de Sturges (que generó un mayor número de intervalos)
podría ser más adecuado para representar estos datos, ya que permite visualizar mejor la posible
bimodalidad en la distribución de los rendimientos. Sin embargo, la elección del número de
intervalos “ideal” es subjetiva y depende del objetivo del análisis. Si se busca una visión general
de la distribución, el criterio de la raiz podría ser suficiente (redondeo al entero más cercano).
Si se busca identificar detalles o variaciones, el criterio de la regla de Sturges podría ser más
apropiado para obtener un equilibrio entre la visualización de la forma general de la distribución y
la detección de posibles patrones.
Si ahora lo que se quiere es combinar algunas de estas gráficas en mayor número en una única
ventana se puede añadir al código de las representaciones realizadas hasta ahora lo siguiente:
168
Programación en Python: Estadística a través de problemas resueltos
En el caso de más gráficos se ha de tener en cuenta que fig es el contenedor global de los
gráficos. Si fueran más filas y columnas, axs sería una matriz en lugar de una lista, y se accedería
con axs[fila, columna]. axs es un array con los subgráficos individuales, accesibles como
axs[0], axs[1], etc. pasaría a ser axs[0,0] para la primera fila y primera columna. Tal como se
muestre en la figura 23.
import [Link] as plt
import numpy as np
# 4. Histograma (Subgráfico 3)
# Generamos 1000 datos aleatorios con distribución normal
datos = [Link](1000)
169
Programación en Python: Estadística a través de problemas resueltos
170
Programación en Python: Estadística a través de problemas resueltos
axs[1, 0].set_title("Histograma")
axs[1, 0].set_xlabel("Valor")
axs[1, 0].set_ylabel("Frecuencia")
Sin embargo, la función boxplot proporciona varios argumentos para personalizar el gráfico de
cajas por defecto:
171
Programación en Python: Estadística a través de problemas resueltos
meanline: presenta la media aritmética como una línea de color verde por defecto si su
valor es True.
showfliers = False: hace que no aparezcan los valores atípicos en el gráfico.
patch_artist = True: modifica el color de las cajas de un gráfico de cajas que por
defecto es blanco. Puede sobrescribirse estableciendo el valor a True y pasando un diccionario
con facecolor al argumento boxprops, tal y como se muestra en el siguiente bloque de
código.
medianprops: El color por defecto de la línea que representa la mediana es naranja, pero
puede cambiarse pasando un diccionario a medianprops. También se puede modificar el
grosor de la línea de esta forma.
Así pues, otra representación de los datos anteriores es la siguiente, en donde se define el gráfico
bp para aplicarle una personalización visual que se comenta en el propio código que sigue a
continuación (figura 25):
import [Link] as plt
import numpy as np
172
Programación en Python: Estadística a través de problemas resueltos
# Personalización visual
# Color de la caja
[Link](bp['boxes'], facecolor='lightblue', edgecolor='black')
# Color y grosor de la mediana
[Link](bp['medians'], color='red', linewidth=2)
# Color y grosor de la media
[Link](bp['means'], color='green', linewidth=2)
# Color y grosor de los bigotes
[Link](bp['whiskers'], color='gray', linewidth=1.5)
# Color y grosor de los límites
[Link](bp['caps'], color='black', linewidth=2)
# Mostrar gráfico
[Link]()
Finalmente, para concluir este apartado se presenta el estudio comparativo de varias variables
mediante este tipo de gráficos. Se presenta el caso del estudio de la producción para dos parcelas
(extensible a más) (figura 26):
import [Link] as plt
173
Programación en Python: Estadística a través de problemas resueltos
[Link]()
Gráficos de violín
Para la creación del gráfico de violin violinplot() se va a partir de 50 datos para cada parcela
que siguen una distribución normal cada una. Entre los argumentos que se utilizan se tienen las
opciones:
showmeans = True → Muestra un marcador para la media.
showmedians = True → Muestra un marcador para la mediana.
showextrema = True → Muestra los valores mínimos y máximos.
bw_method = 0.5 → Controla la suavidad de la forma del violín. Y para personalizarlo:
[Link](vp['bodies'][0],facecolor='lightblue',edgecolor='black'):
primera variable de color azul claro.
[Link](vp['bodies'][1],facecolor='lightgreen',edgecolor='black'):
segunda variable de color verde claro.
[Link](vp['cmeans'], edgecolor='red', linewidth = 2): Color de la me-
dia en rojo
[Link](vp['cmedians'], edgecolor = 'green', linewidth = 2): Color de
la mediana en verde
174
Programación en Python: Estadística a través de problemas resueltos
# Datos de producción (kg/ha) para cada parcela, modificados para mayor diferencia
# Media 3500, desviación estándar 300
parcela_a = [Link](3500, 300, 50)
# Media 4500, desviación estándar 500
parcela_b = [Link](4500, 500, 50)
El gráfico de violín es una herramienta útil porque combina información de un diagrama de cajas
con una distribución de densidad, lo que permite visualizar tanto la dispersión como los valores
centrales (media y mediana) en una misma representación.
El gráfico de violín permite visualizar con mayor claridad las diferencias en la distribución de la
producción de trigo entre las dos parcelas. Se observa que la Parcela B tiene un rendimiento
promedio superior al de la Parcela A, sin embargo, esta ventaja viene acompañada de una mayor
variabilidad en la producción, lo que sugiere que sus rendimientos son menos predecibles.
Desde un punto de vista estadístico, la Parcela A muestra una distribución más concentrada
alrededor de su media, lo que indica mayor estabilidad en la producción. En contraste, la Parcela
B presenta una mayor dispersión en sus valores, lo que puede deberse a factores como variabilidad
en el suelo, riego o condiciones climáticas.
175
Programación en Python: Estadística a través de problemas resueltos
Es importante recordar que en este caso los datos han sido generados aleatoriamente y no
corresponden a un estudio real. En un análisis práctico, sería necesario trabajar con datos reales
obtenidos mediante mediciones en campo para extraer conclusiones válidas y tomar decisiones
informadas.
El uso de este tipo de gráfico es útil para representar series de tiempo con varias categorías.
Permite observar tendencias generales y comparativas e la producción. La personalización de
colores, leyendas y cuadrículas mejor la interpretación visual. Con stackplot() se va a crear un
gráfico de áreas apiladas para visualizar la evolución de la producción de tres cultivos relevantes
en la provincia de Almería durante un período de 5 años.
import [Link] as plt
import numpy as np
176
Programación en Python: Estadística a través de problemas resueltos
[Link]()
Se observa un incremento progresivo en la producción de los tres cultivos entre 2021 y 2025. El
tomate es el principal cultivo, seguido por el pimiento y el pepino. El crecimiento es aproximada-
mente lineal en los tres casos. Este monitoreo de producción permite visualizar la evoluación de la
producción de cultivos.
El crecimiento en los tres cultivos es uniforme, lo que sugiere estabilidad en la producción.
Un crecimiento estable en la producción sugiere buenas condiciones de mercado y tecnología
agrícola avanzada.
177
Casos aplicados de análisis de datos y la
probabilidad: de estadística descriptiva a
la inferencia estadística
En este capítulo se abordarán los conceptos estadísticos desde una doble perspectiva: por un lado,
desarrollando los algoritmos que los sustentan, y por otro, utilizando librerías especializadas que
permiten evidenciar su potencia y facilitar su comprensión. El objetivo es que estos conceptos
resulten accesibles y útiles para la formación del lector. En línea con el propósito general del libro,
se ofrece un tratamiento elemental pero completo de la estadística, abarcando temas como la
estadística descriptiva, tablas de frecuencias unidimensionales y bidimensionales, independencia y
correlación, variables aleatorias, probabilidades e inferencia estadística.
178
Programación en Python: Estadística a través de problemas resueltos
179
Programación en Python: Estadística a través de problemas resueltos
Cada una de estas librerías tiene sus fortalezas y debilidades dependiendo del contexto de uso.
NumPy es ideal para operaciones numéricas rápidas y eficientes sobre colecciones de datos, Pandas
destaca en el manejo de datos estructurados, y SciPy es adecuada cuando se requieren técnicas
estadísticas más avanzadas. La elección dependerá del tipo de análisis que se desee realizar, de la
estructura de los datos y de las necesidades del proyecto. Sin embargo, dominar las tres librerías
y entender cómo combinar sus capacidades permitirá realizar análisis estadísticos poderosos y
flexibles en cualquier área, desde la ingeniería agronómica hasta la economía o la biología.
Una vez introducidas las principales ideas y casos de uso de las librerías, se procede a la resolución
de distintos problemas. El enfoque se centrará en las medidas descriptivas, esenciales para resumir
y comprender las características básicas de un conjunto de datos.
180
Programación en Python: Estadística a través de problemas resueltos
Asimetría y curtosis
Diseñar un programa en Python que calcule la asimetría y la curtosis de un conjunto de datos
dado desde cuatro enfoques diferentes:
1. Cálculo manual: Implementar las fórmulas matemáticas de asimetría y curtosis directamente
sin utilizar librerías especializadas.
2. Utilizando NumPy: Aprovechar las funciones de la librería NumPy para realizar los cálculos
necesarios.
3. Utilizando SciPy: Emplear las funciones específicas para asimetría y curtosis disponibles
en la librería SciPy.
4. Utilizando Pandas: Convertir los datos a una Serie de Pandas y usar sus métodos integrados
para calcular la asimetría y la curtosis.
import numpy as np
import pandas as pd
import [Link] as stats
def curtosis(datos):
n = len(datos)
media = [Link](datos)
s = [Link](datos, ddof=1)
return (n * (n+1) / ((n-1) * (n-2) * (n-3))) * \
[Link](((data - media) / s) ** 4) - (3 * (n-1) ** 2) / ((n-2) * (n-3))
def curtosis_numpy(datos):
n = len(datos)
media = [Link](datos)
s = [Link](datos, ddof=1)
return (n * (n + 1) / ((n - 1) * (n - 2) * (n - 3))) * \
[Link](((data - media) / s) ** 4) - (3 * (n-1) ** 2) / ((n-2) * (n-3))
181
Programación en Python: Estadística a través de problemas resueltos
Regresión lineal
import random
import statistics as st
Por último, se le puede agregar el código del gráfico sin necesidad de incorporar ninguna librería
adicional utilizando [Link](x,y) para crear el diagrama de dispersión para cada par de
puntos (xi , yi ). El código y el resultado podrían ser tal que así:
182
Programación en Python: Estadística a través de problemas resueltos
Probabilidad
Ejercicio resuelto: Experimento dado En el experimento aleatorio de lanzar un dado, calcular las
probabilidades de ser par, impar y la probabilidad de la intersección de sucesos.
Solución sin comprensión de listas
E = list(range(1, 7)) # Espacio muestral
n = len(E) # Total de la muestra
183
Programación en Python: Estadística a través de problemas resueltos
def main():
print("Calculadora de Probabilidad")
print("===========================")
while True:
print("\nSeleccione una opción:")
print("1. Calcular la unión de dos eventos: P(A U B)")
print("2. Calcular la probabilidad condicionada: P(A | B)")
print("3. Salir")
if opcion == "1":
prob_A = validar_probabilidad("Introduzca el valor de P(A): ")
prob_B = validar_probabilidad("Introduzca el valor de P(B): ")
prob_interseccion = validar_probabilidad("Introduzca el valor \
de P(A intersecccion B): ")
union = calcular_union(prob_A, prob_B, prob_interseccion)
if union is None:
print("Error: Revise las probabilidades de los sucesos.")
else:
print(f"\n\tP(A U B) = {union:.4f} ")
184
Programación en Python: Estadística a través de problemas resueltos
else:
print("Opción no válida. Intente de nuevo.")
# Ejecutar el programa
main()
En muchos lenguajes de programación como Java, C o C++, cuando se quiere hacer un menú de
opciones, se suele usar una estructura switch o switch-case para ejecutar diferentes bloques
de código según el valor de una variable.
Sin embargo, en Python no existe switch. Aunque ya se ha usado a lo largo del contenido de
este libro una estructura if-elif-else tradicional en los menús, esto puede hacer que el código
crezca en complejidad y repetición. Una alternativa más elegante y permitida en Python es usar
un diccionario de funciones, con lo que se mejora la legibilidad y escalabilidad del código, y es
muy útil para programas educativos, menús interactivos, simulaciones, juegos de texto, etc.
Así pues, se utilizarán: - Las claves del diccionario para representar las opciones del menú. - Los
valores del diccionario son las funciones que se deben ejecutar.
Cuando el usuario elija una opción, se llamará a la función asociada con esa clave. Es decir, el
valor de la clave en el diccionario tiene que ser el mismo que el nombre de la función def.
...
menu = {
"1": f_opcion_1,
"2": f_opcion_2,
"0": salir
}
if opcion in menu:
menu[opcion]()
185
Programación en Python: Estadística a través de problemas resueltos
def simular_precipitacion():
lluvia = [Link](0, 100) # mm de lluvia simulada
print(f"\n Precipitación simulada: {lluvia:.2f} mm")
def estimar_rendimiento():
rendimiento = [Link](5000, 400) # media: 5000 kg/ha, sigma: 400
print(f"\n Rendimiento estimado del cultivo: {rendimiento:.2f} kg/ha")
def calcular_estadisticas():
datos = input("\nIntroduce una serie de datos separados por espacios: ")
try:
numeros = [float(x) for x in [Link]()]
media = [Link](numeros)
desviacion = [Link](numeros)
print(f"- Media: {media:.2f}")
print(f"- Desviación estándar: {desviacion:.2f}")
except:
print("- Error: Asegúrate de introducir números válidos separados por espacios.")
def simular_plagas():
plagas = ['pulgón', 'araña roja', 'trips']
probabilidades = [0.5, 0.3, 0.2]
observadas = [Link](plagas, weights=probabilidades, k=5)
print(f"\n- Plagas observadas: {observadas}")
def salir():
print("\n---> Saliendo del programa. ¡Hasta pronto!")
# Diccionario de opciones
menu = {
"1": simular_precipitacion,
"2": estimar_rendimiento,
"3": calcular_estadisticas,
"4": simular_plagas,
"0": salir
}
186
Programación en Python: Estadística a través de problemas resueltos
if opcion in menu:
menu[opcion]()
if opcion == "0":
break
else:
print("---> Opción no válida. Intenta de nuevo.")
Ejercicio resuelto: Implementar en Python un ejemplo que permita simular la Ley de los grandes
números. La Ley de los Grandes Números establece que, al repetir un experimento aleatorio un
gran número de veces, la frecuencia relativa de un evento tiende a estabilizarse en torno a su
probabilidad teórica. Utiliza la simulación del lanzamiento de una moneda. A modo de ejemplo
aquí se presenta una de las simulaciones:
187
Programación en Python: Estadística a través de problemas resueltos
[Link]("Frecuencia Relativa")
[Link]("Simulación de la Ley de los Grandes Números")
[Link]()
[Link]()
# Parámetros de la simulación
prob_evento = 0.5 # Probabilidad de obtener "Cara"
num_lanzamientos = 10000 # Cantidad de lanzamientos de la moneda
# Ejecutar la simulación
frecuencia_final = simular_ley_grandes_numeros(prob_evento, num_lanzamientos)
print(f"Frecuencia relativa final después de {num_lanzamientos} \
experimentos: {frecuencia_final:.4f}")
Ejercicio resuelto: Teorema de Bayes. Crear un programa que calcule la probabilidad de que una
planta esté realmente mente enferma sabiendo que la sensibilidad del test con el que se hizo haya
dado positivo. El test de detección no es perfecto y se quiere calcular la probabilidad real de la
planta. El programa debe pedir las entradas:
188
Programación en Python: Estadística a través de problemas resueltos
Ejercicio resuelto: Desarrolla un programa en Python que permita clasificar variables en función
de su tipo (cuantitativa o categórica), su escala de medida (nominal, ordinal, intervalo o razón) y su
dependencia (dependiente o independiente). El programa funcionará como un juego interactivo en
el que el usuario deberá responder correctamente a preguntas sobre diferentes variables relacionadas
con la agronomía.
El programa trabajará con variables tales como:
Temperatura del suelo en grados Celsius (cuantitativa, independiente)
Rendimiento de un cultivo en kg/ha (cuantitativa, dependiente)
Tipo de fertilizante usado (A, B, C) (cualitativa, independiente)
Calidad del suelo (buena, media, mala) (cualitativa, independiente)
Nivel de pH del suelo (cuantitativa, independiente)
Tiempo de germinación de las semillas (días) (cuantitativa, dependiente)
Entre las reglas de juego se seleccionaran tres variables aleatorias, se formularán tres preguntas por
variable, el usuario recibirá indicación de si es correcta o no su respuesta, calculará la puntuación
total siendo:
9 puntos: ¡Excelente! Eres un experto en clasificación de variables.
6-8 puntos: ¡Muy bien! pero puedes mejorar.
Menos de 6 puntos: sigue practicando para mejorar tu conocimiento en estadística.
# Definición de las variables y sus clasificaciones correctas
variables = [
("Temperatura del suelo en grados Celsius", "cuantitativa", "independiente"),
("Rendimiento de un cultivo en kg/ha", "cuantitativa", "dependiente"),
("Tipo de fertilizante usado (A, B, C)", "cualitativa", "independiente"),
("Calidad del suelo (buena, media, mala)", "cualitativa","independiente"),
("Nivel de pH del suelo", "cuantitativa", "independiente"),
("Tiempo de germinación de las semillas (días)", "cuantitativa", "dependiente")
]
print(f"\nVariable: {variable}")
189
Programación en Python: Estadística a través de problemas resueltos
print("\t¡Correcto!")
puntos += 1
else:
print(f"\t¡Incorrecto! Es {tipo_correcto}.")
return puntos
def jugar():
"""Función principal del juego."""
print("\n\tBienvenido al juego 'Clasifica la Variable'")
print("Responde correctamente sobre el tipo y la dependencia de cada variable.")
# Evaluación de desempeño
if puntuacion == 6:
print("¡Excelente! Eres un experto en clasificación de variables.")
elif puntuacion >= 4:
print("¡Muy bien! ¡Pero aún puedes mejorar!")
else:
print("¡Sigue practicando para mejorar tu conocimiento sobre estadística!")
Distribuciones de probabilidad
Ejercicio resuelto: Se desea contrastar los efectos de los tamaños de los parámetros n y p en la
forma de representación de una distribución Binomial B(n, p):
1. Para p pequeña y n pequeña, la distribución binomial es sesgada hacia la derecha.
2. Para p grande y n pequeña, la distribución binomial es sesgada hacia la izquierda.
3. Para p=0,5 y n grande y pequeña, la distribución binomial e simétrica.
4. Para p pequeña y n grande, la distribución binomial se acerca a la simetría.
5. A medida que n crece la distribución binomial se aproxima a una normal.
El programa que permite visualizar estas características en su forma puede ser el siguiente:
import numpy as np
import [Link] as plt
import math
190
Programación en Python: Estadística a través de problemas resueltos
for i in range(len(lista_n)):
n = lista_n[i]
p = lista_p[i]
pmf = [funcion_masa_binomial(n, p, k) for k in valores_x]
[Link](valores_x, pmf, marker='o', linestyle='-', label=f'n={n}, p={p}')
[Link]()
[Link]("Distribución Binomial y Muestras Simuladas")
[Link]("Número de éxitos")
[Link]("Probabilidad / Frecuencia relativa")
[Link]()
[Link]()
return None
def main():
while True:
try:
cantidad_valores=int(input("¿Cuántas distribuciones Binomiales deseas introducir? "))
if cantidad_valores <= 0:
print("El número debe ser mayor que 0.")
continue
except ValueError:
print("Por favor, introduce un número válido.")
continue
lista_n = []
lista_p = []
for i in range(cantidad_valores):
try:
n = int(input(f"Introduce el valor de n[{i+1}]: "))
p = float(input(f"Introduce el valor de p[{i+1}]: "))
if not (0 <= p <= 1):
print("El valor de p debe estar entre 0 y 1.")
continue
lista_n.append(n)
lista_p.append(p)
except ValueError:
print("Entrada no válida. Inténtalo de nuevo.")
continue
graficar_binomial_y_muestras(lista_n, lista_p)
if __name__ == "__main__":
main()
La comparación del histograma de frecuencias permite observar que, a medida que aumenta el
tamaño de la muestra, la distribución empírica se va acercando cada vez más a la distribución
teórica, lo que refleja el comportamiento descrito por la Ley de los Grandes Números. A medida
que el número de observaciones crece, las frecuencias relativas tienden a estabilizarse y coincidir
con las probabilidades teóricas. Además, una forma de verificar esta aproximación es superponer
una curva normal sobre la distribución binomial. Esto permite visualizar cómo la distribución
binomial se ajusta a una distribución normal con media µ = n · p y varianza σ 2 = n · p · q.
191
Programación en Python: Estadística a través de problemas resueltos
Después de ejecutar el programa varias veces con distintos valores de p, ¿qué se observa en la
forma de la distribución cuando p es cercano a 0 o a 1?
import numpy as np
import [Link] as plt
import math
192
Programación en Python: Estadística a través de problemas resueltos
[Link](figsize=(6, 5))
def main():
while True:
try:
p = float(input("Introduce la probabilidad de éxito p (0 < p <= 1): "))
if not (0 < p <= 1):
print("El valor de p debe estar entre 0 y 1.")
continue
tamano = int(input("Introduce el número de muestras a generar: "))
if tamano <= 0:
print("El número de muestras debe ser mayor que 0.")
continue
except ValueError:
print("Entrada no válida. Inténtalo de nuevo.")
continue
mostrar_bernoulli(p, tamano)
Utilizar una distribución de Poisson en lugar de una Binomial (cuando se puede) es doble:
La Binomial B(n, p) depende de dos parámetros mientras que la Poisson P (λ) sólo de uno.
El cálculo de la función de probabilidad de la Poisson es más fácil de calcular que una
binomial.
193
Programación en Python: Estadística a través de problemas resueltos
import numpy as np
import [Link] as plt
import [Link] as stats
# Simulaciones
muestras_binomial = [Link](num_ensayos, prob_exito, size=num_simulaciones)
muestras_poisson = [Link](lambda_poisson, size=num_simulaciones)
# Histograma
[Link](figsize=(10, 6))
bins = [Link](min(muestras_binomial.min(), muestras_poisson.min()),
max(muestras_binomial.max(), muestras_poisson.max()) + 1.5) - 0.5
def main():
"""Función principal para ejecutar la simulación."""
print("-"*75)
print("Recuerde que para que la aproximación sea válida: n>=20 y p<0.05")
print("y que n.p permanece constante.")
print("-"*75)
while True:
try:
num_ensayos = int(input("Introduzca el número de ensayos (n): "))
prob_exito = float(input("Introduzca la probabilidad de éxito (p): "))
if __name__ == "__main__":
main()
194
Programación en Python: Estadística a través de problemas resueltos
normal por definición, y represente dos gráficas, una en la que se observen varias normales con la
misma media y distintas varianzas, y otro gráfico donde se visualicen distribuciones normales con
distinta media y varianzas iguales.
import numpy as np
import [Link] as plt
for mu in medias:
axes[1].plot(x, densidad_normal(x, mu, desviacion_fija), label=f'$\mu$={mu}')
195
Programación en Python: Estadística a través de problemas resueltos
1 x2
f (x) = √ e− 2
2π
def probabilidad_cola_izquierda_manual(z):
"""
Calcula la probabilidad con cola a la izquierda para una distribución
normal estándar utilizando una aproximación numérica.
Argumentos:
z: El valor en el que se quiere calcular la probabilidad.
196
Programación en Python: Estadística a través de problemas resueltos
Retorno:
La probabilidad con cola a la izquierda.
"""
# Aproximación de la integral utilizando la regla del trapecio
# 1. Definir el número de segmentos (trapecios) para la aproximación
num_segmentos = 1000
def probabilidad_cola_izquierda_scipy(z):
"""
Calcula la probabilidad con cola a la izquierda para una distribución
normal estándar utilizando la función cdf de SciPy.
Argumentos:
z: El valor en el que se quiere calcular la probabilidad.
Retorno:
La probabilidad con cola a la izquierda.
"""
from [Link] import norm # norm aquí para minimizar dependencias
return [Link](z)
# Ejemplo de uso
z = 1.96 # Valor de ejemplo
p_manual = probabilidad_cola_izquierda_manual(z)
p_scipy = probabilidad_cola_izquierda_scipy(z)
197
Programación en Python: Estadística a través de problemas resueltos
while True:
# Solicitar datos al usuario
media = float(input("Introduce la media de la distribución: "))
desviacion_tipica = float(input("Introduce la desviación típica \
de la distribución: "))
198
Programación en Python: Estadística a través de problemas resueltos
Para la realización de este ejercicio se crea a partir de una población dada, todas las muestras de
tamaño inferior a ésta, de manera que se irá calculando las medias y de las desviaciones típicas de
la distribución de medias para cada uno de los tamaños de la muestra. Finalmente, se contrasta
su valor con la media de la población y con la desviación típica para hallar la relación pedida.
import itertools
import numpy as np
import pandas as pd
for n in tamanos_muestra:
muestras = list([Link](poblacion, repeat=n))
medias = [[Link](muestra) for muestra in muestras]
desviaciones = [[Link](muestra, ddof=0) for muestra in muestras]
media_de_medias = [Link](medias)
desviacion_tipica_de_medias = [Link](medias)
data = {
'Muestras': muestras,
'Media': medias,
'Desviación Típica': desviaciones,
}
df = [Link](data)
# Almacenar datos de la muestra en el diccionario con key tamaño de la muestra
resultados[n] = df
print("-" * 75)
print(f"Resultados para muestras de tamaño {n}:")
print([Link]())
print([Link]())
print("\n\tMedia de la distribución de Medias:", media_de_medias)
print("\tDesviación Típica de la distribución de Medias:", desviacion_tipica_de_medias)
print(f"\n--- Comparación con la población ---")
print(f"\tMedia de la población: {[Link](poblacion):.2f}")
print(f"\tDesv. típica población entre raiz tamaño muestra {n}: \
{[Link](poblacion)/[Link](n)}\n")
return resultados
# Ejemplo de uso:
poblacion = [5, 6, 7, 8]
tamanos_muestra = [2, 3, 4]
resultados = calcular_distribucion_de_medias(poblacion, tamanos_muestra)
199
Programación en Python: Estadística a través de problemas resueltos
En este ejercicio se han combinado distintas estructuras de datos y librerías para conseguir
comprobar empíricamente la relación. Este programa permite modificar la población así como los
distintos tamaños muestrales. La salida del programa permite comprobar la relación indicada.
---------------------------------------------------------------------------
Resultados para muestras de tamaño 3:
Muestras Media Desviación Típica
0 (5, 5, 5) 5.000000 0.000000
1 (5, 5, 6) 5.333333 0.471405
2 (5, 5, 7) 5.666667 0.942809
3 (5, 5, 8) 6.000000 1.414214
4 (5, 6, 5) 5.333333 0.471405
Muestras Media Desviación Típica
59 (8, 7, 8) 7.666667 0.471405
60 (8, 8, 5) 7.000000 1.414214
61 (8, 8, 6) 7.333333 0.942809
62 (8, 8, 7) 7.666667 0.471405
63 (8, 8, 8) 8.000000 0.000000
---------------------------------------------------------------------------
Resultados para muestras de tamaño 4:
Muestras Media Desviación Típica
0 (5, 5, 5, 5) 5.00 0.000000
1 (5, 5, 5, 6) 5.25 0.433013
2 (5, 5, 5, 7) 5.50 0.866025
3 (5, 5, 5, 8) 5.75 1.299038
4 (5, 5, 6, 5) 5.25 0.433013
Muestras Media Desviación Típica
251 (8, 8, 7, 8) 7.75 0.433013
252 (8, 8, 8, 5) 7.25 1.299038
253 (8, 8, 8, 6) 7.50 0.866025
254 (8, 8, 8, 7) 7.75 0.433013
255 (8, 8, 8, 8) 8.00 0.000000
200
Programación en Python: Estadística a través de problemas resueltos
Teorema de Moivre-Laplace
Una variable aleatoria X con distribución binomial con parámetros n (número de ensayos) y p
(probabilidad de éxito). Entonces, para n suficientemente grande, la distribución de X se puede
√
aproximar mediante una distribución normal N µ = np, σ = npq .
La variable estandarizada es:
Z = √X−np
np(1−p)
q = 1 - p
criterio1 = n * p >= 5 and n * q >= 5 # Condición clásica
criterio2 = p < 0.1 and n * p > 5 # Pequeño p pero np suficientemente grande
criterio3 = p > 0.1 and n * p < 5 # Probabilidad no demasiado pequeña pero np pequeño
criterio4 = abs(p - 0.5) < 0.1 and n * p > 3 # p cercano a 0.5 con np moderado
201
Programación en Python: Estadística a través de problemas resueltos
[Link]("Número de éxitos")
[Link]("Densidad de probabilidad")
[Link](titulo)
[Link]()
[Link](True)
[Link]()
def main():
"""Función principal para ejecutar la simulación."""
while True:
try:
n = int(input("Introduce el número de ensayos (n): "))
p = float(input("Introduce la probabilidad de éxito (p): "))
simular_aproximacion_normal(n, p)
break # Salir del bucle si los datos son válidos
except ValueError as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
Queda al lector comprobar distintos casos de verificación de la aproximación. Una posible prueba
o caso de validación es el siguiente:
202
Programación en Python: Estadística a través de problemas resueltos
203
Programación en Python: Estadística a través de problemas resueltos
204
Programación en Python: Estadística a través de problemas resueltos
Ejercicio resuelto: Constraste de hipótesis para media con varianza conocida. Crear un programa
en Python que realice un contraste de hipótesis para la media de una población con varianza
conocida. El programa deberá:
Consideraciones:
Utilizar las funciones [Link]() y [Link]() del módulo [Link] para calcular
el valor crítico y el p-valor, respectivamente.
Implementar un control de errores para asegurar que el tamaño de la muestra introducido
por el usuario sea un número entero.
Mostrar mensajes claros e informativos al usuario durante la ejecución del programa.
Documentar el código con comentarios que expliquen la lógica del programa.
Caso de prueba:
Tamaño de la muestra: 30
Media muestral: 170
Desviación estándar de la población: 10
Media hipotética: 165
Nivel de significación: 0.05
Tipo de hipótesis alternativa: Unilateral derecha
205
Programación en Python: Estadística a través de problemas resueltos
import numpy as np
import [Link] as stats
def contraste_hipotesis_media_varianza_conocida():
"""
Realiza un contraste de hipótesis para la media de una población con varianza conocida.
"""
# Mostrar resultados
print("\nResultados del contraste de hipótesis:")
print(f"Estadístico de prueba (Z): {estadistico_z:.2f}")
print(f"Valor crítico: {valor_critico:.2f}")
print(f"p-valor: {p_valor:.3f}")
print(f"{decision} la hipótesis nula (H0).")
# Ejecutar la función
contraste_hipotesis_media_varianza_conocida()
206
Programación en Python: Estadística a través de problemas resueltos
def minmaxtabla(serie) :
# Realizar el conteo de las frecuencias para la tabla y nombrar columnas (objeto Series)
tabla_frec = serie.value_counts().sort_index().reset_index()
tabla_frec.columns = ['Valor', 'Frecuencia']
# Resumen de estadísticos
print("\tResumen de estadísticos")
print("==============================================")
print(f"El valor mínimo es {[Link]()} y el máximo {[Link]()}")
print(f"\nLa media aritmética es {[Link]()}, la cuasi-desviación típica {[Link]()}, \
y la cuasi-varianza {[Link]()}")
# Resumen descriptivo tamaño, media, desviación típica, mínimo, y cuartiles
print(tabla_frec.describe())
# Tabla de frecuencias
print("\n\tTabla de frecuencias relativas")
print("==============================================")
minmaxtabla(s)
Variedad Tm
cherry 103.5
kumato 94.2
pera 113.9
corazón 94.1
raf 84.3
muchamiel 103.8
roma 104.0
daniela 84.4
negro 94.1
marzano 84.2
207
Programación en Python: Estadística a través de problemas resueltos
Calcula:
Rendimiento medio, desviación típica y varianza.
Muestra las variedades que tienen un rendimiento superior al del promedio.
Haz la representación de un gráfico de línea.
import pandas as pd
# Se declaran los datos como un diccionario
datos = {
'cherry': 103.5,
'kumato': 94.2,
'pera': 113.9,
'corazon': 94.1,
'raf': 84.3,
'muchamiel': 103.8,
'roma': 104.0,
'daniela': 84.4,
'negro': 94.1,
'marzano': 84.2
}
Ejercicio resuelto: Escribir una función que reciba un diccionario con las notas de los alumno de
un curso y devuelva una serie con la nota mínima, la máxima, media y la desviación típica.
import pandas as pd
def estadistica_notas(notas):
notas = [Link](notas)
estadisticos = [Link]([[Link](), [Link](), [Link](), \
[Link]()], index=['Min', 'Max', 'Media', 'Desviación típica'])
return estadisticos
import pandas as pd
def estadistica_notas(notas):
208
Programación en Python: Estadística a través de problemas resueltos
notas = [Link](notas)
return [Link]()
X 1 2 3 4 5 6
Y 2 5 9 13 17 21
En primer lugar, se construyen las listas como objetos Series de la librería pandas con los datos
del enunciado, que representan a la variable independiente X y a la dependiente Y :
X = [Link]([1, 2.5, 3.5, 4, 5, 6], name="X")
Y = [Link]([2, 5.5, 9.5, 15, 16, 21],name="Y")
Para el cálculo de la covarianza hay que rectificar el valor que aporta pandas con el factor
n−1
n
. También se necesita convertir las series a un DataFrame para poder utilizar el método que
calcula la covarianza.
# Unión de las dos series en un DataFrame
df = [Link]([X,Y], axis=1)
# Matriz de covarianzas
[Link]()
209
Programación en Python: Estadística a través de problemas resueltos
[Link]()
Ejercicio resuelto. Suponga que se tienen los siguientes datos de una cosecha de trigo en
diferentes regiones:
datos = {
"Región": ["A", "B", "C", "D"],
"Cosecha (toneladas)": [300, 500, 400, 450],
"Área sembrada (hectáreas)": [150, 250, 200, 225]
}
df = [Link](datos)
También se puede hacer un gráfico de dispersión para visualizar la relación entre la cosecha y el
área sembrada:
[Link](df["Área sembrada (hectáreas)"], df["Cosecha (toneladas)"])
[Link]("Relación entre área sembrada y cosecha")
[Link]("Área sembrada (hectáreas)")
[Link]("Cosecha (toneladas)")
[Link]()
210
Programación en Python: Estadística a través de problemas resueltos
Ejercicio resuelto. Se tienen los datos de la producción de maíz en kg/ha en diferentes regiones
de un país durante los últimos 5 años. Se quiere realizar un análisis estadístico utilizando la librería
Pandas de Python y visualizar los datos con matplotlib.
import pandas as pd
import [Link] as plt
datos = {
"Región A": [800, 950, 900, 1000, 1100],
"Región B": [700, 800, 850, 950, 1000],
"Región C": [600, 700, 750, 800, 900],
"Región D": [650, 750, 800, 900, 950]
}
# Resumen estadístico
print([Link]())
print(producciones_medias)
[Link](regiones, producciones_medias)
[Link]()
[Link]("Producción media de maíz por región")
[Link]("Regiones")
[Link]("Producción (kg/ha)")
[Link]()
211
Programación en Python: Estadística a través de problemas resueltos
212
Programación en Python: Estadística a través de problemas resueltos
Este caso de estudio se centrará en las etapas que aparecen con mayúscula: procesamiento,
análisis y visualización utilizando diversas librerías, en concreto Pandas y algo de visualización
con Matplotlib, pero a escala “casita de pájaros”.
Lo habitual es trabajar con un conjunto de librerías que facilitan en tratamiento y análisis de los
datos, que se denomina ecosistema PyData. Esta compuestos por las siguientes librerías:
Pandas que incluye desde transformaciones de datos, como ordenar filas y tomar subcon-
juntos, sobre tablas de datos (DataFrames).
NumPy para cálculo numérico, de hecho Pandas esta contruido sobre Numpy
Matplotlib, Seaborn, Plotlyy otros paquetes de visualización de datos
scikit-learn para aprendizaje automático
parcelas – datos de las parcelas bajo la RAIF (Red de alerta e información fitosanitaria),
cuyas columnas son:
CAMPAÑA; PROVINCIA; MUNICIPIO; CODPARCELA; Comarca; Paraje; Zona; U_H_C; Tipo_Explotación;
Sistema_Gestión; Superficie; Pendiente; Orientación; Tipo_suelo; Tipo_invernadero; Cubierta;
Fecha_plástico; Tipo_plástico; Sistema_nebulización; Doble_puerta; Ventilación; Malla;
Especie; Variedad; Cultivo_precedente; Incidencias_cultivo_anterior; Riego;
Procedencia_agua; Calidad_agua; Limpieza_exterior; Limpieza_interior; Cuba_independiente;
Cumplimiento_Lucha_biológica; Num_Campañas; Representa_U_H_C; Zona_Biológica_RAIF;
Secano; Codigo_invernadero; Instalaciones_pulverización; Coordenada_UTM;
Coordenada_Y_UTM; Altitud; Estación_climática; Otro_plástico; Calefacción; Sensor
213
Programación en Python: Estadística a través de problemas resueltos
producción histórica. Datos de superficies y toneladas producidas del 2013 al 2024 en los
cultivos de algodón y trigo en Andalucía.
producto; fecha; [Link]; [Link]
def leer_csv(nombre_archivo):
rutaActual = Path(__file__).[Link]()
rutaDatos = rutaActual / nombre_archivo
df = pd.read_csv(rutaDatos, sep=";")
[Link](0, [Link], inplace=True)
return df
if __name__ == "__main__":
214
Programación en Python: Estadística a través de problemas resueltos
muestreos = leer_csv('[Link]')
parcelas = leer_csv('[Link]')
produccion = leer_csv('[Link]')
historico = leer_csv('[Link]')
Si se está usando el entorno de desarrollo Google Colaboratory (Colab), y suponiendo que los
datos están en una carpeta llamada datos_produccion el código a utilizar es
import pandas as pd
import numpy as np
from pathlib import Path
import os
def leer_csv(nombre_archivo):
ruta = carpeta_drive / nombre_archivo
try:
df = pd.read_csv(ruta, sep=";")
[Link](0, [Link], inplace=True)
print(f" Archivo '{nombre_archivo}' leído correctamente.")
return df
except Exception as e:
print(f" Error al leer '{nombre_archivo}': {e}")
return None
Al igual que Pandas puede importar datos de varios tipos de archivos, también permite exportar
datos a varios formatos. Esto ocurre especialmente cuando los datos se transforman mediante
Pandas y necesitan guardarse localmente en la máquina. A continuación se explica cómo convertir
los DataFrames de pandas a varios formatos.
Un DataFrame de pandas (aquí estamos utilizando df) se guarda como un archivo CSV mediante
el método .to_csv(). Los argumentos incluyen el nombre del archivo con la ruta, donde index
= True implica escribir el índice del DataFrame.
df.to_csv("[Link]", index=False)
df.to_excel("[Link]", index=False)
215
Programación en Python: Estadística a través de problemas resueltos
También se pueden cambiar los cuartiles con el argumento percentiles. Aquí, por ejemplo, se
muestran los percentiles 30 %, 50 % y 70 % de las columnas numéricas de df.
[Link](percentiles=[0.3, 0.5, 0.7])
Se pueden aislar tipos de datos específicos en la salida resumida con el argumento include. Aquí,
por ejemplo, solo se resumen las columnas con el tipo de datos entero (ìnt).
[Link](include=[int])
216
Programación en Python: Estadística a través de problemas resueltos
[Link]
list([Link]) # lo convierte en una lista
muestreos = leer_csv('[Link]')
parcelas = leer_csv('[Link]')
produccion = leer_csv('[Link]')
print([Link]())
print('\n')
print([Link](n=10))
print('\n')
print([Link])
print('\n')
print([Link](percentiles=[0.3, 0.5, 0.7]))
print('\n')
print([Link](include=[float]))
print('\n')
print([Link]().T)
print('\n')
print( [Link](show_counts=True, memory_usage=True, verbose=True))
print('\n')
print([Link])
print('\n')
print([Link][0])
print('\n')
print([Link][1])
print('\n')
print(list([Link])) # lo convierte en una lista
print('\n')
print(list([Link]))
print('\n')
print([Link]().head(8))
print('\n')
217
Programación en Python: Estadística a través de problemas resueltos
list(parcelas['Especie'])
Utilizando un operador > se establecen comparaciones. El código siguiente busca en las parcelas
aquellas de más de una hectárea obteniendo la campaña, la provincia, el municipio y el código de
parcela.
[Link][parcelas['Superficie'] > 10000, ['CAMPAÑA','PROVINCIA','MUNICIPIO','CODPARCELA']]
218
Programación en Python: Estadística a través de problemas resueltos
Del mismo modo, la mediana de cada columna se calcula con el método .median()
[Link]()
219
Programación en Python: Estadística a través de problemas resueltos
muestreos = leer_csv('[Link]')
parcelas = leer_csv('[Link]')
produccion = leer_csv('[Link]')
historico = leer_csv('[Link]')
print(list(set(parcelas['Especie'])))
print('\n')
print(parcelas[[Link]==4])
print('\n')
print(parcelas[[Link](range(2,5))])
print('\n')
print(muestreos[[Link] == "M. blanca: % plantas con presencia"])
print('\n')
print(parcelas[[Link] == 'Pimiento'])
print('\n')
print([Link][parcelas['Superficie'] > 10000, ['CAMPAÑA','PROVINCIA',\
'MUNICIPIO','CODPARCELA']])
print('\n')
print(produccion)
print('\n')
produccionlimpia = [Link](subset=['[Link]'])
print(produccionlimpia)
print('\n')
parcelas2023 = parcelas[[Link]ÑA == 2023]
print([Link](numeric_only=True))
print('\n')
print([Link]())
print('\n')
print([Link](numeric_only=True))
print('\n')
historico['rendimiento'] = historico['producción.T']/historico['[Link]']
print([Link]())
print('\n')
print(parcelas['MUNICIPIO'].value_counts())
print(parcelas['MUNICIPIO'].value_counts(normalize=True))
calabacin_protegido = produccion[produccion['Producto'] == 'Calabacín protegido']
produccion_media = calabacin_protegido.groupby('Provincia')['[Link]'].mean().reset_index()
produccion_media.columns = ['Provincia', 'Produccion_Media_TM']
print(produccion_media)
Mediante la función .plot() suministramos una lista para su trazado y .show() muestra el
220
Programación en Python: Estadística a través de problemas resueltos
# Figura 37
figura = [Link]()
[Link](clima_est[['T_Med', 'H_R_Med']])
o bien
# Figura 38
[Link](clima_est['T_Med'], label='T_Med') # primera columna
[Link](clima_est['H_R_Med'], label='H_R_Med')
Gráficos de barras
Mostrar un gráfico de barras por provincia agrupando los datos de las tres estaciones de medida.
# Figura 40
temperatura_media = [Link]('provincia')['T_Med'].mean().reset_index()
[Link](temperatura_media['provincia'], temperatura_media['T_Med'],\
label='Temperatura Media (°C)')
[Link]('Provincia')
[Link]('Valor promedio')
[Link]('Temperatura')
221
Programación en Python: Estadística a través de problemas resueltos
222
Programación en Python: Estadística a través de problemas resueltos
[Link]('mi_grafico_temperatura.png')
[Link]()
[Link](figsize=(12, 6))
# Configurar el gráfico
[Link]('Provincia', fontsize=12)
[Link]('Valor promedio', fontsize=12)
[Link]('Temperatura y Humedad Media por Provincia', fontsize=16)
[Link](x, temperatura_media['provincia'], rotation=45) # Mostrar nombres de provincias
[Link]()
[Link](axis='y')
plt.tight_layout()
[Link]()
Gráfico de sectores
Distribución de producciones medias:
# Figura 42
produccion = [Link](subset=['[Link]'])
produccion_media = [Link](['Provincia', 'Producto'])\
223
Programación en Python: Estadística a través de problemas resueltos
224
Programación en Python: Estadística a través de problemas resueltos
['[Link]'].mean().reset_index()
produccion_media.columns = ['Provincia', 'Producto', 'Produccion_Media_TM']
print(produccion_media)
provincia_seleccionada = 'ALMERÍA' # Cambia esto según la provincia que desees visualizar
Histograma
Por ejemplo, sobre las temperaturas de una estación meteorológica dada:
# Filtrar los datos de la estación "AL001" en Almería
# Figura 43
almeria_al001 = clima[clima['cod_est'] == 'AL001']
plt.tight_layout()
[Link]()
225
Programación en Python: Estadística a través de problemas resueltos
[Link](figsize=(10, 6))
[Link](spider_almeria['valor'], bins=10, color='skyblue', edgecolor='black')
[Link]('Histograma de Araña Roja en Almería', fontsize=16)
[Link]('Porcentaje de plantas con presencia de M. blanca ( %)', fontsize=12)
[Link]('Frecuencia', fontsize=12)
[Link](axis='y', alpha=0.75)
[Link]()
notas = [4, 8, 7.5, 6, 5.5, 5.2, 3.5, 7.7, 3.2, 9, 6.8, -2]
[Link](notas)
[Link]("Boxplot con Matplotlib")
[Link]()
226
Programación en Python: Estadística a través de problemas resueltos
clima = leer_csv('[Link]')
clima_est = clima[clima['cod_est'] == 'AL001']
# Asegurarse de que la columna 'T_Med' sea numérica y convertir valores inválidos en NaN
clima_est['T_Med'] = pd.to_numeric(clima_est['T_Med'], errors='coerce')
[Link](figsize=(8, 6))
[Link](clima_est['T_Med'], vert=False, showmeans=True)
[Link]("Boxplot de Temperatura Media en la Estación AL001")
[Link]("Temperatura Media (°C)")
[Link]()
Elaboración de modelos
El modelo de regresión lineal trata de encontrar una línea o una fórmula que mejor describa cómo
una variable (la que se conoce) afecta a la otra (la que se quiere predecir). Esta fórmula se basa
en los datos que que existen y permite hacer predicciones sobre nuevos datos.
A continuación, se muestra el proceso para estudiar la producción de trigo y algodón considerando
superficie cultivada y producción buscando detactar relaciones entre los valores.
import pandas as pd
import [Link] as plt
import numpy as np
import os
from leer import leer_csv
227
Programación en Python: Estadística a través de problemas resueltos
[Link](Y)
[Link]()
[Link](titulo)
[Link]()
[Link]([Link](script_dir, '[Link]'))
[Link]()
# Mostrar los dos gráficos en un mismo panel, uno al lado del otro
mostrar_en_panel(trigo, '[Link]', 'produccion.T', 'prediccion_trigo', \
'Regresión para Trigo', algodon, '[Link]', \
'produccion.T', 'prediccion_algodon', 'Regresión para Algodón')
228
Programación en Python: Estadística a través de problemas resueltos
229