CURSO

Python para Ciencia de Datos

Ph.D. Antonio Escamilla P.
9 sections to go

Capítulo 1: Introducción al Lenguaje Python

Python para Ciencia de Datos

Python se ha convertido en el lenguaje de programación dominante en la ciencia de datos, y no es por casualidad. Su combinación única de simplicidad, legibilidad y potencia lo ha transformado en la herramienta preferida por analistas, científicos de datos e investigadores en todo el mundo.

¿Por qué Python es crucial en la ciencia de datos?

En este curso, construiremos desde los fundamentos del lenguaje hasta las técnicas avanzadas de manipulación y análisis de datos. Aprenderás a transformar datos crudos en conocimientos accionables, descubrir patrones ocultos y comunicar tus hallazgos de manera efectiva a través de visualizaciones impactantes.

La habilidad de programar en Python para ciencia de datos te permitirá abordar problemas complejos, tomar decisiones basadas en evidencia y generar valor en prácticamente cualquier industria o campo de investigación. ¡Comencemos este fascinante viaje al mundo del análisis de datos con Python!

La Historia de Python

Contrario a lo que muchos piensan, el nombre del lenguaje Python no hace referencia a la serpiente, sino a una peculiar fuente de inspiración: el grupo cómico británico "Monty Python". Guido van Rossum, el creador del lenguaje, era fan del programa de televisión "Monty Python's Flying Circus" y decidió nombrar su creación en honor a este show.

Python nació a finales de los años 80 como un proyecto personal de Van Rossum mientras trabajaba en el Centro para las Matemáticas y la Informática (CWI) en los Países Bajos. Su objetivo era crear un lenguaje que fuera accesible para principiantes pero lo suficientemente potente para los expertos. La primera versión pública (Python 0.9.0) se lanzó en febrero de 1991.

Guido van Rossum, creador de Python
Guido van Rossum, el creador de Python

A lo largo de su evolución, Python ha mantenido sus principios fundamentales de diseño, resumidos en "El Zen de Python" (accesible escribiendo import this en cualquier intérprete de Python). Este conjunto de 19 aforismos incluye ideas como "Lo simple es mejor que lo complejo" y "La legibilidad cuenta", filosofías que han contribuido enormemente a la popularidad del lenguaje en el campo de la ciencia de datos.

El espíritu lúdico que inspiró el nombre de Python permanece como parte integral de su cultura. La comunidad Python valora la diversión y la claridad, lo que se refleja en la documentación oficial, donde abundan referencias humorísticas y ejemplos con elementos de la cultura popular. Este enfoque accesible y amigable ha sido clave para convertir a Python en el lenguaje ideal para principiantes y expertos por igual.

Cómo instalar Python

Antes de comenzar a programar, necesitas tener Python instalado y un ambiente de desarrollo en tu computadora. Puedes seguir nuestra guía de instalación de Python para descargar e instalar las versiones adecuadas para tu sistema operativo.

Instalación de Python
Instalación de Python y VSCode

Comenzando con Python

Ahora que conoces un poco sobre Python, comencemos a experimentar. Una de las formas más sencillas de trabajar con Python es utilizando un intérprete o "shell", un entorno donde puedes escribir código Python y ver inmediatamente los resultados.

Empecemos con algo simple y usemos Python como una calculadora:

# Una simple operación aritmética
4 + 5
9

El intérprete de Python interpreta lo que escribiste y muestra el resultado de tu cálculo, 9. El shell que usamos habitualmente para trabajar es IPython (Python Interactivo), una versión mejorada del intérprete estándar de Python que incluye muchas funcionalidades útiles para la ciencia de datos.

IPython fue creado por Fernando Pérez y forma parte del ecosistema más amplio de Jupyter, herramientas fundamentales en la ciencia de datos moderna.

Scripts de Python

Además de trabajar interactivamente con Python, también puedes ejecutar scripts de Python. Estos son simplemente archivos de texto con la extensión .py que contienen una lista de comandos Python que se ejecutan secuencialmente, casi como si estuvieras escribiendo los comandos en el shell tú mismo, línea por línea.

Veamos un ejemplo simple de script:

# Este es un script de Python simple
# Realizamos algunos cálculos básicos
resultado = 4 + 5
print("El resultado es:", resultado)
El resultado es: 9

Observa que en los scripts, necesitas usar explícitamente la función print() si quieres generar alguna salida. Colocar tu código en scripts de Python en lugar de escribir cada paso interactivamente te ayudará a mantener la estructura y evitará tener que reescribir todo una y otra vez si quieres hacer un cambio; simplemente haces el cambio en el script y ejecutas todo de nuevo.

Si no utilizas la función print() en tus scripts, no verás ningún resultado en la salida, aunque el código se ejecute correctamente.

Comenzando tu viaje

Ahora que tienes una idea de las diferentes formas de trabajar con Python, estás listo para comenzar. Te recomendamos usar el intérprete para experimentar y probar ideas rápidas, mientras que los scripts son ideales para soluciones más permanentes y complejas.

A lo largo de este curso, iremos construyendo tus habilidades de programación para ciencia de datos paso a paso. No te preocupes si no tienes experiencia previa en programación; Python es conocido por ser amigable para principiantes y el enfoque práctico de este curso te ayudará a adquirir confianza rápidamente.

¡Comencemos este fascinante viaje juntos y no olvides divertirte mientras aprendes!

Tipos de Datos Básicos en Python

Python maneja diversos tipos de datos que son fundamentales para almacenar y manipular información. Cada tipo de dato tiene sus propias características y operaciones asociadas.

Números

Python tiene varios tipos numéricos que utilizaremos constantemente en análisis de datos:

# Números enteros (int)
5
-10
42

# Operaciones con enteros
print(10 + 5)     # Suma
print(20 - 7)     # Resta
print(6 * 8)      # Multiplicación
print(100 // 6)   # División entera
print(17 % 5)     # Módulo (resto de la división)
print(2 ** 8)     # Potenciación (2 elevado a 8)
15
13
48
16
2
256

Las operaciones con números enteros son precisas y eficientes. Observa que la división normal (/) siempre devuelve un número de punto flotante, mientras que la división entera (//) devuelve un entero truncando la parte decimal.

# Números de punto flotante (float)
3.14
-0.001
2.5e6  # Notación científica: 2.5 × 10^6

# Operaciones con flotantes
print(3.5 + 2.1)  # Suma
print(9.8 - 4.2)  # Resta
print(2.5 * 3.0)  # Multiplicación
print(7.0 / 2.0)  # División
print(2.5 ** 2.0) # Potenciación con flotantes
5.6
5.6000000000000005
7.5
3.5
6.25
Las operaciones con números de punto flotante pueden tener pequeños errores de redondeo debido a la forma en que las computadoras representan números decimales internamente.

Booleanos

Los valores booleanos representan la verdad lógica: True (verdadero) o False (falso). Son fundamentales para crear condiciones y expresiones lógicas:

# Valores booleanos
True
False

# Operaciones lógicas
print(True and False)  # AND lógico
print(True or False)   # OR lógico
print(not True)        # NOT lógico

# Operaciones de comparación que devuelven booleanos
print(5 > 3)      # Mayor que
print(10 <= 10)   # Menor o igual que
print(7 == 7)     # Igualdad
print(5 != 10)    # Desigualdad
False
True
False
True
True
True
True
Los booleanos son esenciales en las estructuras de control de Python, como las condiciones if y los bucles while, que veremos más adelante.

Strings (Cadenas de texto)

Las cadenas de texto, o strings, son secuencias de caracteres encerrados entre comillas:

# Strings
print("Hola, mundo")
print('Python para Ciencia de Datos')
Hola, mundo
Python para Ciencia de Datos

La función type()

Podemos utilizar la función type() para determinar el tipo de dato de cualquier valor en Python:

# Verificando diferentes tipos
print(type(42))
print(type(3.14))
print(type(True))
print(type("Hola"))
<class 'int'>
<class 'float'>
<class 'bool'>
<class 'str'>
Conocer el tipo de datos con los que trabajas es fundamental en ciencia de datos, ya que diferentes tipos admiten diferentes operaciones y puede afectar el resultado de tus análisis.

Variables en Python

Una de las características fundamentales de Python es su manejo de variables. En Python, no necesitas declarar el tipo de variable antes de usarla, lo que hace que el código sea más limpio y fácil de escribir.

# Asignación de variables
nombre = "Carlos"
edad = 28
peso = 70.5

# Asignación múltiple
x, y, z = 10, "Hola", True

print(nombre, edad, peso)
print(x, y, z)
Carlos 28 70.5
10 Hola True

Como puedes ver, Python infiere el tipo de la variable según el valor que le asignas. Esto se conoce como "tipado dinámico".

En Python, puedes cambiar el tipo de una variable simplemente asignándole un valor de otro tipo, algo que no es posible en lenguajes de tipado estático como Java o C++.

Convenciones de nomenclatura

Para nombrar variables en Python, es importante seguir estas convenciones:

# Ejemplos de nombres de variables válidos
mi_variable = 10
contador1 = 0
_variable_privada = "secreto"

# Ejemplos de nombres de variables inválidos
# 1variable = 20  # No puede comenzar con número
# mi-variable = 30  # No puede contener guiones
# mi variable = 40  # No puede contener espacios
En Python, se utiliza la convención "snake_case" para nombrar variables y funciones: palabras en minúsculas separadas por guiones bajos.

Variables y comportamiento según el tipo

En Python, es importante entender que los operadores pueden comportarse de manera diferente dependiendo del tipo de datos con el que trabajas. Observa estos ejemplos:

# Suma de enteros
5 + 7

# Concatenación de cadenas
"Hola" + " Python"
12
"Hola Python"

Para los enteros, el operador + realiza una suma aritmética, mientras que para las cadenas (strings), el mismo operador concatena o une los textos. Este es un principio general en Python: el comportamiento del código depende de los tipos de datos con los que estás trabajando.

Podemos almacenar estos valores en variables para reutilizarlos:

# Creando variables con diferentes tipos
numero_a = 5
numero_b = 7
resultado_numeros = numero_a + numero_b

texto_a = "Hola"
texto_b = " Python"
resultado_textos = texto_a + texto_b

# Mostrando los resultados
print(resultado_numeros)
print(resultado_textos)

# Comprobando el tipo de las variables resultado
type(resultado_numeros)
type(resultado_textos)
12
Hola Python
<class 'int'>
<class 'str'>
Al asignar valores a variables, Python determina automáticamente su tipo según el valor asignado. Esto facilita la escritura de código, pero debes tener en cuenta el tipo de datos cuando realices operaciones.

Imprimir con formato

Existen varias formas de formatear strings en Python:

# Usando f-strings (Python 3.6+)
nombre = "Juan"
edad = 25
print(f"Hola, me llamo {nombre} y tengo {edad} años.")

# Usando método format()
print("Hola, me llamo {} y tengo {} años.".format(nombre, edad))

# Usando % (estilo antiguo)
print("Hola, me llamo %s y tengo %d años." % (nombre, edad))
Hola, me llamo Juan y tengo 25 años.
Hola, me llamo Juan y tengo 25 años.
Hola, me llamo Juan y tengo 25 años.
Los f-strings son la forma más moderna y eficiente de formatear strings en Python. Permiten incluir expresiones directamente dentro de las llaves.

Entrada de datos del usuario

Puedes obtener entrada del usuario con la función input():

# Solicitar datos al usuario
nombre = input("Introduce tu nombre: ")
edad = int(input("Introduce tu edad: "))  # Convertir a entero

print(f"Hola {nombre}, tienes {edad} años.")
Introduce tu nombre: María
Introduce tu edad: 28
Hola María, tienes 28 años.
La función input() siempre devuelve un string, por eso necesitamos usar funciones como int() o float() para convertir la entrada a números cuando sea necesario.

Estructuras de Datos en Python

Python ofrece varias estructuras de datos incorporadas que son fundamentales para el análisis de datos. Estas estructuras permiten almacenar, organizar y manipular colecciones de datos de manera eficiente.

Listas

Las listas son una de las estructuras de datos más versátiles en Python. Una lista es una colección ordenada y mutable de elementos que pueden ser de diferentes tipos.

# Crear una lista
numeros = [1, 2, 3, 4, 5]
mixta = [10, "hola", True, 3.14]

# Verificar el tipo
type(numeros)
<class 'list'>

Indexación en Listas y Strings

Los elementos de una lista están indexados, comenzando desde 0 para el primer elemento. También se pueden usar índices negativos para acceder a elementos desde el final.

# Lista de ejemplo
frutas = ["manzana", "banana", "cereza", "durazno", "fresa"]

# Acceder a elementos por índice
print(frutas[0])      # Primer elemento
print(frutas[2])      # Tercer elemento
print(frutas[-1])     # Último elemento
print(frutas[-2])     # Penúltimo elemento
manzana
cereza
fresa
durazno

La indexación también funciona con strings, ya que son secuencias de caracteres:

# Indexación en strings
nombre = "Python"
print(nombre[0])    # Primera letra
print(nombre[-1])   # Última letra
P
n
Si intentas acceder a un índice que no existe, Python generará un error IndexError. Siempre verifica que el índice sea válido antes de acceder a él.

Selección de varios elementos (Slicing)

El slicing permite seleccionar un subconjunto de elementos de una lista o string utilizando la sintaxis [inicio:fin:paso], donde inicio es el índice donde comienza la selección (incluido), fin es el índice donde termina (excluido) y paso define el intervalo entre elementos:

# Slicing de listas
numeros = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Sintaxis: lista[inicio:fin:paso]
print(numeros[2:5])     # Elementos del índice 2 al 4
print(numeros[:4])      # Elementos desde el inicio hasta el índice 3
print(numeros[6:])      # Elementos desde el índice 6 hasta el final
print(numeros[1:8:2])   # Elementos del 1 al 7 con paso 2
print(numeros[::-1])    # Lista invertida
[2, 3, 4]
[0, 1, 2, 3]
[6, 7, 8, 9]
[1, 3, 5, 7]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
El slicing con paso negativo es una forma sencilla de invertir una lista o cadena en Python.

El slicing funciona de la misma manera con strings. Observa los índices positivos y negativos para la palabra "Python":

+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
  0   1   2   3   4   5
 -6  -5  -4  -3  -2  -1
# Slicing con strings
texto = "Python"

# Diferentes formas de hacer slicing
print(texto[0:2])    # Primeros dos caracteres
print(texto[2:])     # Desde el tercer carácter hasta el final
print(texto[:4])     # Desde el inicio hasta el cuarto carácter (excluido)
print(texto[-2:])    # Últimos dos caracteres
print(texto[::2])    # Caracteres con paso 2 (posiciones pares)
print(texto[::-1])   # String invertido
Py
thon
Pyth
on
Pto
nohtyP

Indexación anidada

Puedes tener listas dentro de listas (estructuras anidadas) y acceder a sus elementos utilizando múltiples índices:

# Lista anidada con múltiples niveles
arreglo = [1, 2, 3, [4, 5, ["colombia"]]]  # Lista con otra lista al interior

# Acceder a elementos anidados
print(arreglo[0])             # Primer elemento
print(arreglo[3])             # Cuarto elemento (que es una lista)
print(arreglo[3][0])          # Primer elemento de la lista anidada
print(arreglo[3][2])          # Tercer elemento de la lista anidada (otra lista)
print(arreglo[3][2][0])       # Elemento dentro de la lista doblemente anidada
1
[4, 5, ['colombia']]
4
['colombia']
colombia

Agregar datos en una lista

Las listas son mutables, lo que significa que puedes modificarlas después de crearlas:

# Métodos para agregar elementos
frutas = ["manzana", "banana", "cereza"]

# Agregar un elemento al final
frutas.append("durazno")
print(frutas)

# Insertar un elemento en una posición específica
frutas.insert(1, "arándano")
print(frutas)

# Extender una lista con otra lista
frutas.extend(["fresa", "uva"])
print(frutas)
['manzana', 'banana', 'cereza', 'durazno']
['manzana', 'arándano', 'banana', 'cereza', 'durazno']
['manzana', 'arándano', 'banana', 'cereza', 'durazno', 'fresa', 'uva']

Asignar valores a una posición

Puedes cambiar el valor de un elemento existente asignándole un nuevo valor:

# Modificar elementos por índice
colores = ["rojo", "verde", "azul"]
print(colores)

# Cambiar el segundo elemento
colores[1] = "amarillo"
print(colores)

# También puedes modificar múltiples elementos con slicing
numeros = [1, 2, 3, 4, 5]
numeros[1:4] = [20, 30, 40]
print(numeros)
['rojo', 'verde', 'azul']
['rojo', 'amarillo', 'azul']
[1, 20, 30, 40, 5]

Borrar Elementos de una lista

Existen varios métodos para eliminar elementos de una lista:

# Diferentes formas de eliminar elementos
frutas = ["manzana", "banana", "cereza", "durazno", "fresa"]
print(frutas)

# Eliminar un elemento por su valor
frutas.remove("cereza")
print(frutas)

# Eliminar un elemento por su índice y obtenerlo
fruta_eliminada = frutas.pop(1)
print(f"Se eliminó: {fruta_eliminada}")
print(frutas)

# Eliminar el último elemento
ultima = frutas.pop()
print(f"Último elemento: {ultima}")
print(frutas)

# Eliminar un elemento por índice sin devolverlo
del frutas[0]
print(frutas)
['manzana', 'banana', 'cereza', 'durazno', 'fresa']
['manzana', 'banana', 'durazno', 'fresa']
Se eliminó: banana
['manzana', 'durazno', 'fresa']
Último elemento: fresa
['manzana', 'durazno']
['durazno']
El método remove() elimina solo la primera ocurrencia del valor. Si necesitas eliminar todas las ocurrencias, deberás usar un bucle o comprensión de listas.

Concatenar listas

Puedes combinar listas utilizando el operador + o el método extend():

# Concatenar listas
lista1 = [1, 2, 3]
lista2 = [4, 5, 6]

# Usando el operador +
lista_combinada = lista1 + lista2
print(lista_combinada)

# Usando extend (modifica la lista original)
numeros_originales = [10, 20, 30]
numeros_adicionales = [40, 50]
numeros_originales.extend(numeros_adicionales)
print(numeros_originales)
[1, 2, 3, 4, 5, 6]
[10, 20, 30, 40, 50]
La diferencia entre + y extend() es que el operador + crea una nueva lista, mientras que extend() modifica la lista original.

Diccionarios

Los diccionarios son colecciones no ordenadas de pares clave-valor. Son ideales para almacenar y recuperar datos mediante una clave única.

# Crear un diccionario
persona = {
   "nombre": "Ana",
   "edad": 28,
   "profesion": "Data Scientist",
   "lenguajes": ["Python", "R", "SQL"]
}

# Acceder a valores por clave
print(persona["nombre"])
print(persona["lenguajes"])

# Método get (seguro, devuelve None o un valor predeterminado si la clave no existe)
print(persona.get("salario"))  # La clave no existe
print(persona.get("salario", "No especificado"))  # Valor predeterminado
Ana
['Python', 'R', 'SQL']
None
No especificado

Los diccionarios son mutables, por lo que puedes añadir, modificar o eliminar elementos:

# Modificar un diccionario
persona = {"nombre": "Ana", "edad": 28}

# Agregar nuevos pares clave-valor
persona["ciudad"] = "Barcelona"
persona["email"] = "ana@ejemplo.com"
print(persona)

# Modificar un valor existente
persona["edad"] = 29
print(persona)

# Eliminar un par clave-valor
del persona["email"]
print(persona)

# Obtener todas las claves y valores
print(persona.keys())
print(persona.values())
print(persona.items())  # Pares clave-valor como tuplas
{'nombre': 'Ana', 'edad': 28, 'ciudad': 'Barcelona', 'email': 'ana@ejemplo.com'}
{'nombre': 'Ana', 'edad': 29, 'ciudad': 'Barcelona', 'email': 'ana@ejemplo.com'}
{'nombre': 'Ana', 'edad': 29, 'ciudad': 'Barcelona'}
dict_keys(['nombre', 'edad', 'ciudad'])
dict_values(['Ana', 29, 'Barcelona'])
dict_items([('nombre', 'Ana'), ('edad', 29), ('ciudad', 'Barcelona')])
A diferencia de las listas, los diccionarios no tienen un orden garantizado en versiones de Python anteriores a 3.7. A partir de Python 3.7, los diccionarios mantienen el orden de inserción.

Tuplas

Las tuplas son secuencias inmutables, lo que significa que no pueden modificarse después de crearse. Son similares a las listas, pero se definen con paréntesis en lugar de corchetes.

# Crear tuplas
coordenadas = (10.5, 20.8)
persona = ("Ana", 28, "Ingeniera")

# Tupla con un solo elemento (necesita una coma)
singleton = (42,)

# Verificar el tipo
print(type(coordenadas))
<class 'tuple'>

Aunque las tuplas son inmutables, puedes acceder a sus elementos igual que en las listas:

# Acceder a elementos de una tupla
persona = ("Ana", 28, "Ingeniera")
print(persona[0])  # Primer elemento
print(persona[-1])  # Último elemento

# Desempaquetar una tupla (muy útil)
nombre, edad, profesion = persona
print(f"{nombre} tiene {edad} años y es {profesion}")

# Slicing también funciona con tuplas
coordenadas = (1, 2, 3, 4, 5)
print(coordenadas[1:4])
Ana
Ingeniera
Ana tiene 28 años y es Ingeniera
(2, 3, 4)
Las tuplas son inmutables, por lo que no puedes agregar, eliminar o modificar elementos. Si intentas hacerlo, Python generará un TypeError.
Las tuplas son útiles cuando quieres asegurarte de que una colección de datos no cambie, como coordenadas, configuraciones o claves de diccionarios.

Conversión entre tipos (Cast)

Python permite convertir entre diferentes tipos de datos utilizando funciones de conversión:

# Conversión entre tipos numéricos
entero = 42
flotante = float(entero)  # int a float
print(flotante)

decimal = 3.14
entero_redondeado = int(decimal)  # float a int (trunca, no redondea)
print(entero_redondeado)

# Conversión entre secuencias
lista = [1, 2, 3]
tupla = tuple(lista)  # lista a tupla
print(tupla)

tupla = (4, 5, 6)
lista = list(tupla)  # tupla a lista
print(lista)

# Strings a números y viceversa
numero_str = "123"
numero_int = int(numero_str)  # string a int
print(numero_int + 7)

numero = 456
numero_str = str(numero)  # número a string
print(numero_str + " es un número")
42.0
3
(1, 2, 3)
[4, 5, 6]
130
456 es un número
La conversión de tipos puede provocar pérdida de datos. Por ejemplo, convertir un float a int elimina la parte decimal, y no todos los strings pueden convertirse a números.
# Conversión entre diccionarios y listas
lista_pares = [("nombre", "Juan"), ("edad", 30)]
diccionario = dict(lista_pares)  # lista de tuplas a diccionario
print(diccionario)

diccionario = {"a": 1, "b": 2}
lista_items = list(diccionario.items())  # items del diccionario a lista
print(lista_items)
{'nombre': 'Juan', 'edad': 30}
[('a', 1), ('b', 2)]
La conversión de tipos (casting) es fundamental cuando trabajamos con datos de diferentes fuentes, como archivos CSV, bases de datos o entradas de usuario.

Sentencias de Control en Python

Las sentencias de control permiten modificar el flujo de ejecución de un programa, haciendo que ciertos bloques de código se ejecuten o se omitan según determinadas condiciones, o que se repitan varias veces. A diferencia de otros lenguajes que utilizan llaves {} o palabras clave como begin-end, Python utiliza la indentación (espacios al inicio de cada línea) para definir bloques de código.

La indentación en Python no es solo una cuestión de estilo, sino que es parte fundamental de la sintaxis. Se recomienda usar 4 espacios para cada nivel de indentación, siguiendo la guía de estilo oficial PEP 8.

Condicionales: if, elif, else

Las estructuras condicionales permiten ejecutar diferentes bloques de código dependiendo de si ciertas condiciones son verdaderas o falsas. En Python, utilizamos if, elif (else if) y else para construir estas estructuras.

# Estructura básica de condicionales
edad = 18

if edad < 18:
   print("Eres menor de edad")
elif edad == 18:
   print("Acabas de cumplir la mayoría de edad")
else:
   print("Eres mayor de edad")

# Condicionales anidados
puntuacion = 85

if puntuacion >= 60:
   print("Aprobado")
   if puntuacion >= 90:
       print("Sobresaliente")
   elif puntuacion >= 80:
       print("Notable")
   else:
       print("Bien")
else:
   print("Reprobado")
Acabas de cumplir la mayoría de edad
Aprobado
Notable
Puedes utilizar operadores lógicos como and, or y not para combinar múltiples condiciones en una sola expresión condicional.
# Uso de operadores lógicos en condicionales
edad = 25
tiene_licencia = True

if edad >= 18 and tiene_licencia:
   print("Puede conducir")
else:
   print("No puede conducir")

# Expresiones condicionales compactas (operador ternario)
mensaje = "Puede votar" if edad >= 18 else "No puede votar"
print(mensaje)
Puede conducir
Puede votar

Ciclos for

Los ciclos for en Python son extremadamente versátiles y se utilizan para iterar sobre secuencias (como listas, tuplas, diccionarios, conjuntos o strings) o cualquier objeto iterable. A diferencia de otros lenguajes, el ciclo for de Python se asemeja más al "foreach" de otros lenguajes.

# Iteración básica con for
for i in range(5):
   print(i, end=" ")

print("\n")  # Nueva línea

# Iteración sobre una lista
frutas = ["manzana", "banana", "cereza"]
for fruta in frutas:
   print(f"Me gusta la {fruta}")

# Iteración con índices usando enumerate
for indice, fruta in enumerate(frutas):
   print(f"Índice {indice}: {fruta}")
0 1 2 3 4

Me gusta la manzana
Me gusta la banana
Me gusta la cereza
Índice 0: manzana
Índice 1: banana
Índice 2: cereza

También puedes iterar sobre diccionarios de varias maneras:

# Iteración sobre diccionarios
persona = {"nombre": "Ana", "edad": 28, "ciudad": "Madrid"}

# Iterar sobre las claves
for clave in persona:
   print(clave)

# Iterar sobre los valores
for valor in persona.values():
   print(valor)

# Iterar sobre pares clave-valor
for clave, valor in persona.items():
   print(f"{clave}: {valor}")
nombre
edad
ciudad
Ana
28
Madrid
nombre: Ana
edad: 28
ciudad: Madrid

Ciclos while

Los ciclos while ejecutan un bloque de código mientras una condición específica sea verdadera. Son útiles cuando no sabes de antemano cuántas iteraciones necesitarás.

# Ciclo while básico
contador = 0
while contador < 5:
   print(contador, end=" ")
   contador += 1

print("\n")  # Nueva línea

# Uso de while para validar entrada
"""
# Este código solicitaría entrada al usuario en un entorno interactivo
numero = -1
while numero < 0:
   try:
       numero = int(input("Ingrese un número positivo: "))
       if numero < 0:
           print("El número debe ser positivo.")
   except ValueError:
       print("Por favor, ingrese un número válido.")
"""

# Ejemplo alternativo sin input
numero = -1
intentos = 0
valores = ["-5", "texto", "10"]  # Simulación de entradas

while numero < 0 and intentos < len(valores):
   try:
       numero = int(valores[intentos])
       if numero < 0:
           print(f"El número {numero} debe ser positivo.")
   except ValueError:
       print(f"'{valores[intentos]}' no es un número válido.")
   intentos += 1

if numero >= 0:
   print(f"Número válido: {numero}")
else:
   print("No se pudo obtener un número válido")
0 1 2 3 4

El número -5 debe ser positivo.
'texto' no es un número válido.
Número válido: 10
Ten cuidado con los ciclos while, ya que pueden generar bucles infinitos si la condición nunca se vuelve falsa. Asegúrate de que algo dentro del bucle modifique eventualmente la condición.

Control de ciclos: break y continue

Python proporciona dos instrucciones especiales para controlar el flujo dentro de los ciclos: break y continue. Estas te permiten alterar el comportamiento normal de un ciclo según ciertas condiciones.

break

La instrucción break termina el ciclo actual por completo y transfiere la ejecución a la siguiente instrucción después del ciclo.

# Uso de break en un ciclo for
print("Ejemplo de break con for:")
for i in range(10):
   if i == 5:
       print("¡Encontramos el 5! Saliendo del ciclo.")
       break
   print(i, end=" ")

print("\n")  # Nueva línea

# Uso de break en un ciclo while
print("Ejemplo de break con while:")
contador = 0
while True:  # Ciclo infinito potencial
   print(contador, end=" ")
   contador += 1
   if contador >= 5:
       print("\nLlegamos a 5. Saliendo del ciclo infinito.")
       break
Ejemplo de break con for:
0 1 2 3 4 ¡Encontramos el 5! Saliendo del ciclo.

Ejemplo de break con while:
0 1 2 3 4
Llegamos a 5. Saliendo del ciclo infinito.

continue

La instrucción continue salta el resto del código en la iteración actual y avanza a la siguiente iteración del ciclo.

# Uso de continue en un ciclo for
print("Ejemplo de continue con for:")
for i in range(10):
   if i % 2 == 0:  # Si i es par
       continue
   print(i, end=" ")

print("\n")  # Nueva línea

# Uso de continue en un ciclo while
print("Ejemplo de continue con while:")
contador = 0
while contador < 10:
   contador += 1
   if contador % 2 == 0:  # Si contador es par
       continue
   print(contador, end=" ")
Ejemplo de continue con for:
1 3 5 7 9

Ejemplo de continue con while:
1 3 5 7 9
break es útil cuando encuentras lo que estabas buscando y no necesitas seguir iterando, mientras que continue es útil cuando quieres omitir ciertos elementos en tu procesamiento.

List Comprehension

Las comprensiones de listas (list comprehensions) son una característica poderosa y concisa de Python que te permite crear nuevas listas aplicando una expresión a cada elemento de una secuencia existente, opcionalmente filtrando elementos con una condición.

# Forma básica de list comprehension
numeros = [1, 2, 3, 4, 5]

# Crear una lista con el cuadrado de cada número
cuadrados = [x**2 for x in numeros]
print(cuadrados)

# List comprehension con condición (filtro)
pares = [x for x in numeros if x % 2 == 0]
print(pares)

# Equivalente usando un ciclo for tradicional
cuadrados_tradicional = []
for x in numeros:
   cuadrados_tradicional.append(x**2)
print(cuadrados_tradicional)

# List comprehension con múltiples condiciones
numeros = list(range(1, 11))
filtrados = [x for x in numeros if x % 2 == 0 if x % 3 != 0]
print(filtrados)  # Números pares que no son divisibles por 3

# List comprehension con expresión if-else
clasificados = ["par" if x % 2 == 0 else "impar" for x in numeros]
print(clasificados)
[1, 4, 9, 16, 25]
[2, 4]
[1, 4, 9, 16, 25]
[2, 4, 8, 10]
['impar', 'par', 'impar', 'par', 'impar', 'par', 'impar', 'par', 'impar', 'par']
Las list comprehensions son más eficientes y elegantes que los bucles for tradicionales para crear listas. Sin embargo, para operaciones muy complejas, a veces un bucle for puede ser más legible.

También puedes utilizar list comprehensions para aplanar estructuras de datos anidadas, convirtiendo una lista de listas en una sola lista:

# Aplanar listas anidadas con list comprehension
canastas = [
   ["manzana", "pera", "uva"],           # Frutas
   ["zanahoria", "apio", "lechuga"],     # Vegetales
   ["naranja", "mandarina", "limón"]     # Cítricos
]

# Convertir múltiples canastas en una sola lista de alimentos
todos_alimentos = [alimento for canasta in canastas for alimento in canasta]
print(todos_alimentos)

# Equivalente usando bucles for tradicionales
lista_tradicional = []
for canasta in canastas:
   for alimento in canasta:
       lista_tradicional.append(alimento)
print(lista_tradicional)
['manzana', 'pera', 'uva', 'zanahoria', 'apio', 'lechuga', 'naranja', 'mandarina', 'limón']
['manzana', 'pera', 'uva', 'zanahoria', 'apio', 'lechuga', 'naranja', 'mandarina', 'limón']
Al aplanar listas con comprehensions, el orden de los bucles for es importante: el bucle externo va primero, seguido por el bucle interno. Esto puede parecer contraintuitivo al principio, pero reflexiona sobre cómo escribirías los bucles for anidados normales.

Funciones en Python

Las funciones son bloques de código reutilizables diseñados para realizar una tarea específica. Son fundamentales en programación por varias razones:

En ciencia de datos, las funciones son especialmente importantes para crear procesos analíticos reproducibles y para manipular conjuntos de datos de manera consistente.

Crear Funciones en Python

Las funciones en Python se definen usando la palabra clave def, seguida del nombre de la función y paréntesis que pueden contener parámetros. El bloque de código de la función se indenta después de dos puntos.

# Función básica sin parámetros
def saludar():
   print("¡Hola, bienvenido a Python!")

# Llamada a la función
saludar()
¡Hola, bienvenido a Python!

Las funciones pueden recibir parámetros (datos de entrada) y devolver valores utilizando la palabra clave return:

# Función con parámetros y valor de retorno
def cuadrado(numero):
   return numero ** 2

# Llamada a la función con un argumento
resultado = cuadrado(5)
print(f"El cuadrado de 5 es: {resultado}")

# Función para sumar todos los elementos de una lista
def suma_lista(numeros):
   total = 0
   for num in numeros:
       total += num
   return total

# Probar la función
mi_lista = [1, 2, 3, 4, 5]
print(f"La suma de {mi_lista} es: {suma_lista(mi_lista)}")
El cuadrado de 5 es: 25
La suma de [1, 2, 3, 4, 5] es: 15
Una función puede no tener parámetros, tener parámetros obligatorios, opcionales (con valores predeterminados), o incluso un número variable de parámetros.

Puedes establecer valores predeterminados para los parámetros, lo que los hace opcionales:

# Función con parámetros predeterminados
def potencia(base, exponente=2):
   return base ** exponente

# Llamadas a la función
print(potencia(3))      # Usa el exponente predeterminado (2)
print(potencia(3, 4))   # Especifica un exponente diferente
9
81
En Python, las funciones son "ciudadanos de primera clase", lo que significa que pueden asignarse a variables, pasarse como argumentos a otras funciones y devolverse como resultados.

Funciones Básicas de Python (Built-in Functions)

Python incluye numerosas funciones incorporadas (built-in) que están disponibles sin necesidad de importar módulos. Estas funciones proporcionan funcionalidades esenciales para el manejo de datos y operaciones comunes.

Lista de funciones incorporadas en Python

abs() dict() help() min() setattr()
all() dir() hex() next() slice()
any() divmod() id() object() sorted()
ascii() enumerate() input() oct() staticmethod()
bin() eval() int() open() str()
bool() exec() isinstance() ord() sum()
bytearray() filter() issubclass() pow() super()
bytes() float() iter() print() tuple()
callable() format() len() property() type()
chr() frozenset() list() range() vars()
classmethod() getattr() locals() repr() zip()
compile() globals() map() reversed() import()
complex() hasattr() max() round()

len()

La función len() devuelve el número de elementos en un objeto. Funciona con cadenas, listas, tuplas, diccionarios y otros objetos iterables.

# Ejemplos de uso de len()
print(len("Python"))              # Longitud de una cadena
print(len([1, 2, 3, 4, 5]))       # Número de elementos en una lista
print(len({"a": 1, "b": 2}))      # Número de pares clave-valor en un diccionario
print(len(range(0, 10, 2)))       # Número de elementos en un rango
6
5
2
5

help()

La función help() proporciona documentación integrada sobre objetos, funciones, módulos y tipos de datos en Python. Es una herramienta invaluable para aprender sobre las funcionalidades disponibles.

# Ejemplo de uso de help()
# help(print)  # Muestra ayuda sobre la función print

# Ejemplo de la salida de help para len
print("La función len() devuelve la longitud (número de elementos) de un objeto.")
print("Sintaxis: len(objeto)")
print("Parámetros: objeto - una secuencia (string, lista, tupla, etc.) o colección (diccionario, set, etc.)")
print("Retorna: un entero que representa el número de elementos")
La función len() devuelve la longitud (número de elementos) de un objeto.
Sintaxis: len(objeto)
Parámetros: objeto - una secuencia (string, lista, tupla, etc.) o colección (diccionario, set, etc.)
Retorna: un entero que representa el número de elementos
Puedes usar help() sin argumentos para iniciar una sesión de ayuda interactiva, o proporcionar un objeto específico como argumento para ver su documentación.

range()

La función range() genera una secuencia de números. Es especialmente útil en bucles for y para crear listas de números.

# Ejemplos de uso de range()
 
# range con un solo argumento (fin, exclusivo)
print(list(range(5)))        # 0 a 4

# range con dos argumentos (inicio, fin)
print(list(range(2, 8)))     # 2 a 7

# range con tres argumentos (inicio, fin, paso)
print(list(range(1, 10, 2))) # Números impares del 1 al 9
print(list(range(10, 0, -1))) # Cuenta regresiva de 10 a 1
[0, 1, 2, 3, 4]
[2, 3, 4, 5, 6, 7]
[1, 3, 5, 7, 9]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
range() en Python 3 devuelve un objeto iterable, no una lista. Para ver los valores, necesitas convertirlo a una lista usando list() o iterarlo en un bucle.

enumerate()

La función enumerate() añade un contador a un iterable y devuelve un objeto enumerate que genera pares (índice, valor). Es ideal cuando necesitas tanto el índice como el valor durante la iteración.

# Ejemplos de uso de enumerate()
frutas = ["manzana", "banana", "cereza"]

# Iteración básica con enumerate
for i, fruta in enumerate(frutas):
   print(f"Índice {i}: {fruta}")

# Usando enumerate con un valor inicial para el contador
for i, fruta in enumerate(frutas, start=1):
   print(f"Fruta #{i}: {fruta}")

# Convertir enumerate a una lista de tuplas
print(list(enumerate(frutas)))
Índice 0: manzana
Índice 1: banana
Índice 2: cereza
Fruta #1: manzana
Fruta #2: banana
Fruta #3: cereza
[(0, 'manzana'), (1, 'banana'), (2, 'cereza')]

Métodos en Python

Los métodos son funciones que pertenecen a objetos específicos. A diferencia de las funciones independientes, los métodos están asociados a un tipo de dato particular y se invocan usando la notación de punto: objeto.metodo().

Métodos en Strings

Los strings en Python tienen numerosos métodos para manipular texto. Estos métodos se pueden categorizar en grupos según su funcionalidad.

Métodos de Formato

# Métodos de formato para strings
texto = "python es genial"

# Cambios de capitalización
print(texto.upper())         # Convierte a mayúsculas
print(texto.lower())         # Convierte a minúsculas
print(texto.capitalize())    # Primera letra en mayúscula
print(texto.title())         # Primera letra de cada palabra en mayúscula

# Alineación y relleno
print(texto.center(30, '-')) # Centra el texto en un espacio de 30 caracteres
print(texto.ljust(25, '*'))  # Alinea a la izquierda en un espacio de 25
print(texto.rjust(25, '*'))  # Alinea a la derecha en un espacio de 25
PYTHON ES GENIAL
python es genial
Python es genial
Python Es Genial
-------python es genial-------
python es genial**********
**********python es genial

Métodos de Búsqueda

# Métodos de búsqueda para strings
texto = "Python es un lenguaje de programación versatil y poderoso"

# Verificar contenido
print(texto.startswith("Python"))  # ¿Comienza con 'Python'?
print(texto.endswith("poderoso"))  # ¿Termina con 'poderoso'?
print("lenguaje" in texto)         # ¿Contiene 'lenguaje'?

# Encontrar posiciones
print(texto.find("lenguaje"))      # Posición de 'lenguaje'
print(texto.find("Java"))          # -1 si no se encuentra
print(texto.count("o"))            # Cuenta ocurrencias de 'o'
True
True
True
13
-1
5

Métodos de Sustitución

# Métodos de sustitución para strings
texto = "Python es genial, Python es poderoso"

# Reemplazar texto
print(texto.replace("Python", "JavaScript"))  # Reemplaza todas las ocurrencias
print(texto.replace("Python", "Java", 1))     # Reemplaza solo la primera ocurrencia

# Eliminar espacios en blanco
texto_con_espacios = "   Python   "
print(texto_con_espacios.strip())             # Elimina espacios al inicio y final
print(texto_con_espacios.lstrip())            # Elimina espacios al inicio
print(texto_con_espacios.rstrip())            # Elimina espacios al final
JavaScript es genial, JavaScript es poderoso
Java es genial, Python es poderoso
Python
Python   
   Python

Métodos de Unión y División

# Métodos de unión y división para strings
palabras = ["Python", "es", "genial"]

# Unir strings
print(" ".join(palabras))           # Une con espacios
print("-".join(palabras))           # Une con guiones

# Dividir strings
texto = "Python,Java,C++,JavaScript"
print(texto.split(","))             # Divide por comas
print(texto.split(",", 2))          # Limita a 2 divisiones

# Partir líneas
texto_multilinea = """Línea 1
Línea 2
Línea 3"""
print(texto_multilinea.splitlines()) # Divide por saltos de línea
Python es genial
Python-es-genial
['Python', 'Java', 'C++', 'JavaScript']
['Python', 'Java', 'C++,JavaScript']
['Línea 1', 'Línea 2', 'Línea 3']
Los strings en Python son inmutables, lo que significa que los métodos como replace(), upper(), etc., no modifican el string original, sino que devuelven una nueva copia modificada.

Métodos en Listas

Las listas en Python tienen métodos que permiten modificar su contenido, reorganizar elementos y obtener información sobre ellos.

.append(x)

Añade un elemento al final de la lista.

# Método append()
frutas = ["manzana", "banana"]
frutas.append("cereza")
print(frutas)
['manzana', 'banana', 'cereza']

.pop([i])

Elimina y devuelve el elemento en la posición especificada. Si no se proporciona un índice, elimina y devuelve el último elemento.

# Método pop()
numeros = [10, 20, 30, 40, 50]

# Eliminar y obtener el último elemento
ultimo = numeros.pop()
print(f"Elemento eliminado: {ultimo}")
print(f"Lista actualizada: {numeros}")

# Eliminar y obtener un elemento específico
segundo = numeros.pop(1)
print(f"Segundo elemento: {segundo}")
print(f"Lista actualizada: {numeros}")
Elemento eliminado: 50
Lista actualizada: [10, 20, 30, 40]
Segundo elemento: 20
Lista actualizada: [10, 30, 40]

.copy()

Crea una copia superficial de la lista.

# Método copy()
original = [1, 2, 3]

# Crear una copia de la lista
copia = original.copy()
print(f"Original: {original}, Copia: {copia}")

# Modificar la copia no afecta al original
copia.append(4)
print(f"Original después de modificar la copia: {original}")
print(f"Copia modificada: {copia}")
Original: [1, 2, 3], Copia: [1, 2, 3]
Original después de modificar la copia: [1, 2, 3]
Copia modificada: [1, 2, 3, 4]
copy() crea una copia superficial, lo que significa que los objetos anidados (como listas dentro de listas) siguen siendo referencias compartidas. Para una copia profunda, usa el módulo copy y su función deepcopy().

.sort(reverse=False)

Ordena los elementos de la lista in-place (modifica la lista original).

# Método sort()
numeros = [3, 1, 4, 1, 5, 9, 2]

# Ordenar de menor a mayor
numeros.sort()
print(f"Ordenados ascendente: {numeros}")

# Ordenar de mayor a menor
numeros.sort(reverse=True)
print(f"Ordenados descendente: {numeros}")

# Ordenar cadenas (alfabéticamente)
nombres = ["Zoe", "Ana", "Carlos", "Berta"]
nombres.sort()
print(f"Nombres ordenados: {nombres}")

# Ordenar por longitud de cadena
nombres.sort(key=len)
print(f"Nombres ordenados por longitud: {nombres}")
Ordenados ascendente: [1, 1, 2, 3, 4, 5, 9]
Ordenados descendente: [9, 5, 4, 3, 2, 1, 1]
Nombres ordenados: ['Ana', 'Berta', 'Carlos', 'Zoe']
Nombres ordenados por longitud: ['Ana', 'Zoe', 'Berta', 'Carlos']

Identificar Elementos en la Lista

# Métodos para identificar elementos
frutas = ["manzana", "banana", "cereza", "banana", "durazno"]

# Contar ocurrencias
print(f"Número de bananas: {frutas.count('banana')}")

# Encontrar la posición de un elemento
print(f"Posición de cereza: {frutas.index('cereza')}")

# Verificar si un elemento existe
print(f"¿Hay manzanas? {'manzana' in frutas}")
print(f"¿Hay uvas? {'uva' in frutas}")

# Encontrar la primera ocurrencia en un rango
print(f"Posición de banana desde el índice 2: {frutas.index('banana', 2)}")
Número de bananas: 2
Posición de cereza: 2
¿Hay manzanas? True
¿Hay uvas? False
Posición de banana desde el índice 2: 3

Instalar Paquetes o Librerías

Python tiene un rico ecosistema de paquetes y librerías que extienden sus capacidades. Para utilizar estas librerías, necesitas instalarlas en tu entorno. Hay diferentes formas de hacerlo, dependiendo de tu entorno de trabajo.

Instalación en Línea de Comando

PIP

PIP (Package Installer for Python) es el gestor de paquetes estándar para Python. Permite instalar, actualizar y eliminar paquetes de PyPI (Python Package Index).

# Instalar un paquete
pip install numpy

# Instalar una versión específica
pip install pandas==1.3.0

# Actualizar un paquete
pip install --upgrade matplotlib

# Instalar múltiples paquetes
pip install numpy pandas matplotlib seaborn

# Ver los paquetes instalados
pip list

# Desinstalar un paquete
pip uninstall scipy
Para instalar paquetes en un entorno específico, asegúrate de que ese entorno esté activado antes de ejecutar los comandos pip.

Instalación en Jupyter Notebook

PIP

También puedes instalar paquetes directamente desde un notebook de Jupyter utilizando el comando mágico ! seguido del comando pip.

# Instalar un paquete desde Jupyter Notebook
!pip install numpy

# O usar el comando system
import sys
!{sys.executable} -m pip install pandas

# Verificar la instalación
import numpy as np
print(f"NumPy versión: {np.__version__}")

import pandas as pd
print(f"Pandas versión: {pd.__version__}")
NumPy versión: 1.24.3
Pandas versión: 2.0.2
Después de instalar nuevos paquetes en un notebook de Jupyter, es posible que necesites reiniciar el kernel para poder utilizarlos.

Ejemplos Prácticos

A continuación, se presentan algunos ejemplos que combinan funciones, métodos y paquetes para realizar tareas comunes en ciencia de datos.

# Ejemplo 1: Análisis básico de una lista de números
numeros = [4, 2, 7, 1, 8, 3, 5, 6]

def analizar_lista(lista):
   """Realiza un análisis básico de una lista de números."""
   resultados = {
       "cantidad": len(lista),
       "suma": sum(lista),
       "promedio": sum(lista) / len(lista) if lista else 0,
       "minimo": min(lista) if lista else None,
       "maximo": max(lista) if lista else None,
       "ordenados": sorted(lista)
   }
   return resultados

# Usar la función
analisis = analizar_lista(numeros)
for clave, valor in analisis.items():
   print(f"{clave.capitalize()}: {valor}")

# Ejemplo 2: Procesamiento de texto
texto = "Python es un lenguaje de programación versátil, poderoso y fácil de aprender."

def analizar_texto(texto):
   """Realiza un análisis básico de un texto."""
   palabras = texto.lower().replace(".", "").replace(",", "").split()
   
   resultados = {
       "num_caracteres": len(texto),
       "num_palabras": len(palabras),
       "palabras_unicas": len(set(palabras)),
       "palabra_mas_larga": max(palabras, key=len)
   }
   return resultados

# Usar la función
analisis_texto = analizar_texto(texto)
for clave, valor in analisis_texto.items():
   print(f"{clave.replace('_', ' ').capitalize()}: {valor}")
Cantidad: 8
Suma: 36
Promedio: 4.5
Minimo: 1
Maximo: 8
Ordenados: [1, 2, 3, 4, 5, 6, 7, 8]
Num caracteres: 77
Num palabras: 12
Palabras unicas: 11
Palabra mas larga: programación

Ejercicios Prácticos

Notebooks interactivos en Google Colab donde podrás practicar los conceptos básicos de Python para ciencia de datos. Ideal para consolidar lo aprendido en este capítulo con ejercicios prácticos y ejemplos ejecutables

Material de Consulta y Profundización

Si deseas ampliar tus conocimientos sobre Python, aquí tienes algunos recursos recomendados:

Referencias

Para aprender más sobre Python y su aplicación en ciencia de datos, recomendamos consultar estos recursos oficiales:

Recuerda que la mejor manera de aprender Python es practicando constantemente. Los recursos listados aquí son solo el punto de partida en tu viaje de aprendizaje. Te animamos a experimentar con el código, modificarlo, romperlo y arreglarlo. Cada error es una oportunidad para aprender algo nuevo. A medida que avances, encontrarás que la comunidad de Python es extremadamente acogedora y dispuesta a ayudar, así que no dudes en unirte a foros, grupos de estudio o contribuir a proyectos de código abierto para seguir desarrollando tus habilidades.