Saltar al contenido principal
InicioTutorialesCiencia de datos

Programación funcional frente a programación orientada a objetos en el análisis de datos

Explore dos de los paradigmas de programación más utilizados en la ciencia de datos: la programación orientada a objetos y la programación funcional.
may 2024  · 15 min leer

Una buena estructura es el sello distintivo de un código bien escrito. La naturaleza de esta estructura viene determinada por el paradigma de programación utilizado. Un paradigma de programación es un conjunto coherente de principios y técnicas que definen el enfoque adoptado a la hora de diseñar y codificar programas informáticos.

Existen muchos paradigmas de programación, como el procedimental, el imperativo, el declarativo, el orientado a objetos y el funcional. Algunos lenguajes se limitan a unos pocos paradigmas. Por ejemplo, SQL es un lenguaje declarativo. Sin embargo, muchos lenguajes modernos, incluido Python, admiten varios paradigmas de programación, lo que permite una mayor flexibilidad.

Practique la escritura de funciones puras con un ejercicio práctico de nuestro curso Introducción a los paradigmas de programación. 

Muchos programadores prefieren por defecto un paradigma de programación, a menudo el que aprendieron primero. Los programadores más avanzados comprenden las ventajas y desventajas de estos paradigmas de programación y pueden aprovechar cada uno de sus puntos fuertes en diferentes escenarios. Repasemos dos de los paradigmas de programación más utilizados en la ciencia de datos: la programación funcional y la programación orientada a objetos.

image1.png

¿Qué es la programación funcional (PF)?

En la programación funcional, el código se construye definiendo y utilizando funciones que dictan cómo debe operar el programa. Es un subtipo del paradigma de programación declarativa.

Las funciones son líneas de código que reciben argumentos y devuelven resultados. Una función simple puede tomar una lista de números como argumento y devolver sólo los valores pares, por ejemplo.

def get_even_numbers(list):
    return [num for num in list if num % 2 == 0]

Echa un vistazo a estos cursos para aprender las mejores prácticas para escribir funciones en Python y R.

La programación funcional destaca por su trazabilidad y previsibilidad. Esto se debe a que las funciones utilizadas son inmutables, lo que significa que no pueden ser alteradas. Las funciones se definen, a menudo en una sección separada del código (o a veces en un archivo diferente), y luego se utilizan en todo el código según sea necesario. Este atributo facilita la determinación de lo que ocurre en una sección de código, ya que la función actúa de la misma manera y se llama de la misma forma en todas partes.

El concepto de inmutabilidad se extiende a las estructuras de datos en la programación funcional. En FP, es preferible crear una copia de los datos cuando se cambia su estado. Así se establece un rastro virtual de todas las transformaciones aplicadas a los datos, lo que mejora la trazabilidad y simplifica la identificación de problemas. Sin embargo, es importante tener en cuenta que este enfoque suele provocar un mayor consumo de memoria, ya que cada cambio de estado implica copiar todo el conjunto de datos.

Un subconjunto de las funciones utilizadas en la programación funcional son las funciones de orden superior. Son funciones que reciben una función como argumento o devuelven una función como resultado. Por ejemplo, puede tener una función de orden superior que ejecute una función varias veces, como la que se muestra a continuación, o una que elija la función correcta para utilizar en un escenario específico.

def run_function_multiple_times(function, num_times):
	for i in range(num_times):
        function()

Ventajas de la programación funcional en el análisis de datos

La programación funcional proporciona una estructura para reducir la duplicación en el código. El objetivo de la programación funcional es facilitar la expresión de acciones repetidas mediante verbos de alto nivel.

Por ejemplo, supongamos que necesita modificar cadenas para adaptarlas a un formato concreto en varios lugares distintos de su código. Este formato requiere que elimine los espacios, los signos de puntuación y los números, y que ponga todo en minúsculas. Podrías copiar y pegar varias líneas de código en diferentes partes de tu programa según sea necesario. Pero esta duplicación hincharía tu código. En su lugar, puede simplemente definir una función para modificar sus cadenas y llamar a esa función en los lugares apropiados. Esto simplifica el código para hacerlo más fino, legible y rápido de escribir.

def clean_string(string):
	exclude = set(string.punctuation + string.digits + " ")
    return ''.join(ch.lower() for ch in string if ch not in exclude)
 
my_string = "Hello, World! 123"
new_string = clean_string(my_string)
print(new_string)

Composición de funciones para construir canalizaciones de datos complejas

La programación funcional es útil cuando se trabaja con grandes conjuntos de datos o pipelines de datos porque nos permite dividir tareas complejas de procesamiento de datos en pasos más pequeños y manejables. A menudo, se creará al menos una función para cada paso del proceso. Si ha escrito un diagrama de flujo de los pasos que necesita en su canal de distribución, tendrá una idea aproximada de las funciones que necesitará. En términos generales, necesitará al menos una función para cada paso.

Diagrama del plan de canalización de datos

La mayoría de los lenguajes también tienen muchas funciones incorporadas y bibliotecas descargables de funciones adicionales. El paquete purr de R es un buen ejemplo. Esto facilita la creación rápida de una canalización y minimiza el número de funciones personalizadas que hay que crear.

Creación de código expresivo y legible utilizando principios de FP

La programación funcional pretende hacer un código muy legible. Existen algunas pautas para construir código expresivo y legible utilizando la programación funcional:

  • Mantenga las funciones reducidas y centradas: Cada función debe tener una responsabilidad clara y única, lo que facilita su comprensión y mantenimiento. Se denominan funciones puras.
  • Evitar el estado mutable (cambiante): Evite cambiar el estado de sus datos para eliminar los efectos secundarios y hacer que el flujo de datos sea más predecible. Las estructuras de datos inmutables contribuyen a la claridad del código y al razonamiento. En lugar de modificar el estado de una estructura, cree una nueva estructura de datos con los cambios deseados.
  • Aproveche las funciones de orden superior: Las funciones que toman otras funciones como argumentos o devuelven funciones se denominan funciones de orden superior. Permiten escribir código más expresivo y conciso.
  • Utilizar la composición de funciones: La composición de funciones es el proceso de combinar dos o más funciones para producir una nueva función. Esto le permite dividir problemas complejos en partes más pequeñas y manejables.

¿Qué es la programación orientada a objetos (POO)?

La programación orientada a objetos es un paradigma que se centra en los objetos. Los objetos son entidades autónomas que constan tanto de datos como de métodos que operan sobre esos datos. Es una forma de mantener los datos y sus operaciones cerca unos de otros, a diferencia de la programación funcional, donde suelen estar separados. Esto puede facilitar el seguimiento de lo que ocurre con sus datos.

En la programación orientada a objetos, los datos y métodos se organizan en objetos definidos por su clase. Las clases se diseñan para dictar cómo debe comportarse cada objeto, y luego los objetos se diseñan dentro de esa clase. Consulte el tutorial de DataCamp para obtener más información sobre el funcionamiento de las clases en Python.

Ejemplo OOP

Por ejemplo, puede tener una clase llamada BankAccount que describa cómo debe comportarse cada cuenta. Esta clase puede tener atributos como el saldo, el número de identificación y el nombre del cliente. También puede tener métodos que describan cómo se puede afectar a la información dentro de esa clase, como la retirada, el depósito y la comprobación del saldo.

Una vez definida esta clase, puede crear un objeto, en este caso, una cuenta para alguien. Una vez creado este objeto, puede rellenarlo con los datos de cada propiedad y utilizar los métodos para modificar la cuenta. Véase el ejemplo siguiente.

# Define a class representing a bank account
class BankAccount:
    def __init__(self, account_id, customer_name, initial_balance=0.0):
        # Initialize account properties
        self.account_id = account_id
        self.customer_name = customer_name
        self.balance = initial_balance

    # Method to deposit money into the account
    def deposit(self, amount):
        """Deposit money into the account."""
        if amount > 0:
            # Update balance and print deposit information
            self.balance += amount
            print(f"Deposited ${amount}. New balance: ${self.balance}")
        else:
            # Print message for invalid deposit amount
            print("Invalid deposit amount. Please deposit a positive amount.")

    # Method to withdraw money from the account
    def withdraw(self, amount):
        """Withdraw money from the account."""
        if 0 < amount <= self.balance:
            # Update balance and print withdrawal information
            self.balance -= amount
            print(f"Withdrew ${amount}. New balance: ${self.balance}")
        elif amount > self.balance:
            # Print message for insufficient funds
            print("Insufficient funds. Withdrawal canceled.")
        else:
            # Print message for invalid withdrawal amount
            print("Invalid withdrawal amount. Please withdraw a positive amount.")

    # Method to check the current balance of the account
    def check_balance(self):
        """Check the current balance of the account."""
        print(f"Current balance for {self.customer_name}'s account (ID: {self.account_id}): ${self.balance}")

Encapsulación

Este proceso de agrupar los datos y el comportamiento en un objeto definido por una clase se denomina encapsulación. Permite crear una estructura bien definida que permite al resto del código interactuar fácilmente con el objeto. Las variables que se almacenan dentro del objeto se denominan atributos, y las funciones que determinan el comportamiento del objeto se denominan métodos.

Herencia

Una forma de reducir la duplicación de código es utilizar la herencia al crear nuevas clases. Se trata de una forma de crear nuevas clases que conservan la funcionalidad de las clases padre. En nuestro ejemplo de cuenta bancaria, podría tener una clase padre generalizada para todas las cuentas y clases hijo con especificidades para cuentas de ahorro y cuentas corrientes.

Polimorfismo

El polimorfismo es un concepto que le permite utilizar el mismo nombre para diferentes métodos que tienen diferentes comportamientos dependiendo de la entrada. Esto se utiliza comúnmente con la herencia.

Por ejemplo, digamos que tenemos una clase padre llamada Shape que tiene un método para calcular el área de la forma. Puede tener dos clases de niños, Círculo y Cuadrado. Mientras que cada uno tendrá el método llamado Área, la definición de ese método será diferente para cada forma. Compara los métodos para el área de las diferentes formas en el bloque de código siguiente.

# Define the parent class Shape
class Shape:
	# Initialize the attributes for the shape
	def __init__(self, name):
    	    self.name = name
	
	# Define a generic method for calculating the area
	def area(self):
    	    print(f"The area of {self.name} is unknown.")
 
# Define the child class Circle that inherits from Shape
class Circle(Shape):
	# Initialize the attributes for the circle
	def __init__(self, name, radius):
    	# Call the parent class constructor
    	    super().__init__(name)
    	    self.radius = radius
	
	# Override the area method for the circle
	def area(self):
    	# Use the formula pi * r^2
    	    area = 3.14 * self.radius ** 2
    	    print(f"The area of {self.name} is {area}.")
 
# Define the child class Square that inherits from Shape
class Square(Shape):
	# Initialize the attributes for the square
	def __init__(self, name, side):
    	# Call the parent class constructor
    	    super().__init__(name)
    	    self.side = side
	
	# Override the area method for the square
	def area(self):
    	# Use the formula s^2
    	    area = self.side ** 2
    	    print(f"The area of {self.name} is {area}.")

Existen varios recursos para aprender a utilizar la programación orientada a objetos, como el curso OOP in Python de DataCamp, el curso OOP in R y el tutorial OOP in Python.

Ventajas de la programación orientada a objetos en el análisis de datos

La programación orientada a objetos puede ofrecer varias ventajas al análisis de datos:

  • Encapsulación de datos: La encapsulación ayuda a ocultar los detalles internos de los algoritmos y estructuras de análisis de datos, exponiendo sólo las interfaces necesarias. Esto favorece una separación limpia de los componentes y mejora la mantenibilidad del código.
  • Reutilización del código: La programación orientada a objetos fomenta la creación de clases genéricas que pueden ser heredadas por otras clases, lo que evita la duplicación de código y reduce el esfuerzo de mantenimiento. Esto puede mejorar la productividad y la eficacia del análisis de datos.
  • Ventajas del diseño: El modelado de problemas del mundo real mediante clases y objetos que representan datos y comportamientos mejora la legibilidad, claridad y modularidad del análisis de datos.

OOP frente a FP: Elegir el paradigma adecuado

Hay muchos factores a tener en cuenta a la hora de elegir entre POO o FP para su proyecto de datos. Algunos equipos de datos pueden preferir firmemente un paradigma al otro, en cuyo caso es preferible que su paradigma coincida con el de su equipo. En otros casos, la naturaleza de sus datos o aplicaciones puede inclinar la balanza en uno u otro sentido.

Cuándo aprovechar la programación orientada a objetos para tareas de datos

La programación orientada a objetos es excelente para aplicaciones en las que es necesario modelar entidades. Algunos ejemplos son el diseño de un sistema para gestionar una biblioteca o la creación de un sistema para puntuar todas las empresas de una cartera de inversión. El proceso de creación de objetos encapsulados facilita el seguimiento de estas entidades. La programación orientada a objetos también es preferible cuando se dispone de memoria limitada, ya que la programación orientada a objetos suele requerir mucha más memoria que la programación orientada a objetos.

Cuándo aprovechar la FP para tareas de datos

La programación funcional destaca en tareas de análisis de datos que requieren un procesamiento paralelo. El énfasis en la inmutabilidad y en evitar los efectos secundarios facilita la paralelización de las operaciones sobre un conjunto de datos. Las funciones pueden aplicarse simultáneamente a distintas partes de los datos sin interferencias.

El enfoque de FP en la inmutabilidad también lo hace ideal para los conductos de transformación de datos. Las funciones pueden componerse para crear una serie de transformaciones sin mutar el estado, lo que da lugar a un código más legible y fácil de mantener.

Los científicos y académicos también suelen preferir FP, ya que se centra más en las matemáticas puras.

Combinación de técnicas POO y FP

En algunas aplicaciones prácticas, OOP y FP pueden combinarse en un programa. Esto permite a los desarrolladores aprovechar los puntos fuertes de cada paradigma. La encapsulación de la programación orientada a objetos y la expresividad de la programación orientada a objetos pueden aprovecharse para construir un canal de aprendizaje automático o una aplicación web.

Sin embargo, combinar paradigmas de este tipo puede plantear problemas y debe hacerse con precaución. Puede ser más difícil descifrar un código que salta entre múltiples paradigmas. También puede ser difícil garantizar una integración fluida de los componentes.

Considere el siguiente ejemplo. Este código python combina OOP y FP, para realizar operaciones matemáticas sobre una lista de números. La clase MathOperation actúa como un contenedor para una operación matemática específica creada usando FP. Las funciones filter_odd_numbers y square_operation ayudan a filtrar los números impares y a calcular el cuadrado de un número, respectivamente. Mediante el uso conjunto de estos componentes, el código filtra eficazmente los números impares de una lista dada y luego los eleva al cuadrado, demostrando cómo la programación orientada a objetos y la FP pueden trabajar juntas para simplificar y organizar las tareas de análisis de datos.

# Object-Oriented Programming (OOP) - Class Definition
class MathOperation:
	def __init__(self, operation):
    	    self.operation = operation
 
	def apply_operation(self, number):
    	    return self.operation(number)
 
# Functional Programming (FP) - Higher-Order Functions
def filter_odd_numbers(numbers):
    return list(filter(lambda x: x % 2 != 0, numbers))
 
def square_operation(number):
    return number ** 2
 
def process_numbers(numbers, operation_function):
    return list(map(operation_function, numbers))
 
# Example Usage:
# Define an OOP class representing a mathematical operation (square in this case)
square_operation_instance = MathOperation(square_operation)
 
# Sample list of numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# Use FP to filter odd numbers
odd_numbers = filter_odd_numbers(numbers)
 
# Use OOP to apply the square operation to odd numbers
result = process_numbers(odd_numbers, square_operation_instance.apply_operation)
 
# Display the result
print(result)

Para otro ejemplo de uso conjunto de POO y FP, echa un vistazo a este repositorio de github.

Conclusión

No existe una respuesta definitiva a qué paradigma de programación es la mejor opción, ya que depende del contexto, el objetivo, el lenguaje y las preferencias del programador. Sin embargo, hay algunas pautas generales que puede seguir para determinar qué paradigma funcionará mejor en su situación.

Utilice la programación orientada a objetos cuando necesite modelar sistemas complejos con múltiples entidades e interacciones, y cuando necesite encapsular datos y comportamientos en componentes reutilizables. Utilice FP cuando necesite realizar cálculos puros con entradas y salidas simples, y cuando necesite evitar efectos secundarios o cambios de estado. Utilice un enfoque mixto cuando necesite aprovechar los puntos fuertes de ambos paradigmas y cuando tenga que adaptarse a las características y limitaciones del lenguaje.

Los mejores programadores no se limitan a un paradigma u otro, sino que se mueven entre ellos según sea necesario para alcanzar sus objetivos. Explore en profundidad una serie de paradigmas con el curso Introducción a los paradigmas de programación. Intente trabajar en un proyecto de datos y utilice tanto la POO como la FP para resolver el proyecto. La práctica de cada paradigma le dará una buena intuición sobre cuál funcionará mejor para su proyecto.

Temas

Comience hoy mismo su viaje a los paradigmas de programación.

Course

Introduction to Programming Paradigms

2 hr
1.7K
Explore a range of programming paradigms, including imperative and declarative, procedural, functional, and object-oriented programming.
See DetailsRight Arrow
Start Course
Ver másRight Arrow
Relacionado

blog

Los mejores lenguajes de programación para los científicos de datos en 2023

¿Estás pensando en introducirte en la ciencia de datos, pero no sabes qué lenguaje de programación elegir? Aquí tienes todo lo que necesitas saber sobre los lenguajes de programación que liderarán el sector de la ciencia de datos en 2023.
Javier Canales Luna's photo

Javier Canales Luna

13 min

blog

5 competencias esenciales en ingeniería de datos

Descubra los conocimientos de ingeniería de datos que necesita para prosperar en el sector. Descubra las funciones y responsabilidades de un ingeniero de datos y cómo puede desarrollar sus propias habilidades.
Joleen Bothma's photo

Joleen Bothma

11 min

blog

11 técnicas de visualización de datos para cada caso de uso con ejemplos

Descubra los análisis, técnicas y herramientas más populares para dominar el arte de la visualización de datos.
Javier Canales Luna's photo

Javier Canales Luna

16 min

Data Analyst surfing on wave of data

blog

9 Competencias esenciales del analista de datos: Guía profesional completa

Aprenda habilidades esenciales de analista de datos, tanto técnicas como interpersonales, desde la programación en Python hasta la comunicación eficaz, para avanzar en su carrera.
Matt Crabtree's photo

Matt Crabtree

9 min

blog

Científico de datos vs. Ingeniero de datos

Explicación de las diferencias entre ingenieros de datos y científicos de datos: responsabilidades, herramientas, lenguajes, perspectivas laborales, salario, etc.
Karlijn Willems's photo

Karlijn Willems

11 min

tutorial

Programación orientada a objetos (POO) en Python: Tutorial

Aborda los fundamentos de la Programación Orientada a Objetos (POO) en Python: explora las clases, los objetos, los métodos de instancia, los atributos y ¡mucho más!
Théo Vanderheyden's photo

Théo Vanderheyden

12 min

See MoreSee More