en Advent of Code, una serie de desafíos de programación diarios lanzados a lo largo de diciembre, por primera vez. Los desafíos diarios suelen contener dos acertijos que se basan en un problema similar. Aunque estos desafíos y problemas no se parecen a los flujos de trabajo típicos de la ciencia de datos, me he dado cuenta de que muchos de los hábitos, formas de pensar y abordar los problemas que fomentan se pueden trasladar sorprendentemente bien al trabajo centrado en datos. En este artículo, reflexiono sobre cinco aprendizajes que obtuve al seguir el desafío Advent of Code de este año y cómo se traducen en ciencia de datos.
Para mí, Advent of Code fue más bien un entorno de práctica controlado para revisar los fundamentos y trabajar en mis habilidades de programación. Te estás concentrando en lo esencial ya que las distracciones que enfrentarías en el trabajo diario no están presentes; no tiene reuniones, cambios de requisitos, comunicación con las partes interesadas ni gastos generales de coordinación. En cambio, tienes un circuito de retroalimentación que es directo y binario: tu respuesta es correcta o no. No existe una solución “casi correcta”, ninguna forma de explicar el resultado ni ninguna forma de vender su solución. Al mismo tiempo, tiene la libertad y flexibilidad de elegir cualquier enfoque que considere adecuado siempre que pueda llegar a una solución correcta.
Trabajar en un entorno así fue todo un desafío, pero muy valioso, ya que también expuso hábitos. Dado que tienes muy poco margen para la ambigüedad y no puedes ocultar tus errores, cualquier defecto en tu trabajo quedó expuesto de inmediato. Con el tiempo, también me di cuenta de que la mayoría de las fallas que encontré tenían poco que ver con la sintaxis, la elección del algoritmo o la implementación de codificación, sino mucho más con la forma en que abordaba los problemas antes de tocar cualquier código. Lo que sigue son mis aprendizajes clave de esta experiencia.
Lección 1: Bosqueje la solución: piense antes de codificar
Un patrón que surgió a menudo durante la llegada del Código fue mi tendencia a pasar directamente a la implementación. Cuando me enfrentaba a un nuevo problema, normalmente me sentía tentado a empezar a codificar inmediatamente e intentar converger hacia una solución lo más rápido posible. Irónicamente, este enfoque a menudo provocaba exactamente lo contrario. Por ejemplo, escribí código profundamente anidado para manejar casos extremos que aumentaban el tiempo de ejecución del código sin darme cuenta de que existía una solución mucho más simple.
Lo que finalmente me ayudó fue dar un paso atrás antes de comenzar con el código. En lugar de ello, comencé observando los requisitos, los aportes y las limitaciones. El proceso de anotar esto me ayudó a obtener un nivel de claridad y estructura que me faltaba cuando salté directamente al código. Además, pensar en posibles enfoques, esbozar una solución aproximada o trabajar en algún pseudocódigo ayudó a formalizar aún más la lógica necesaria. Una vez hecho esto, el acto de implementarlo a través del código se volvió mucho más fácil.
Este aprendizaje se puede trasladar a la ciencia de datos, ya que muchos problemas pueden resultar desafiantes debido a metas poco claras, objetivos mal formulados o porque las limitaciones y los requisitos no se conocen con suficiente antelación. Definir los resultados deseados y razonar sobre la solución antes de comenzar a escribir código puede evitar el desperdicio de esfuerzos. Trabajar hacia atrás desde el resultado previsto en lugar de avanzar desde una tecnología preferida ayuda a mantener el enfoque en el objetivo real que se debe lograr.
Aprendizaje 2: Validación de entradas: conozca sus datos
Incluso después de adoptar este enfoque de esbozar soluciones y definir la solución deseada por adelantado, surgió otro obstáculo recurrente: los datos de entrada. Algunas fallas que experimenté no tuvieron nada que ver con un código defectuoso sino con suposiciones sobre los datos que había hecho y que no se cumplían en la práctica. En un caso, supuse que los datos tenían ciertos límites mínimo y máximo, lo que resultó ser incorrecto y condujo a una solución incorrecta. Después de todo, el código puede ser correcto cuando se ve de forma aislada, pero falla completamente cuando trabaja con datos para los que nunca fue diseñado.
Esto demostró nuevamente por qué es tan crucial verificar los datos de entrada. A menudo, no era necesario renovar completamente mi solución; pequeños ajustes, como introducir condiciones adicionales o comprobaciones de límites, eran suficientes para obtener una solución correcta y sólida. Además, la investigación inicial de datos puede ofrecer señales sobre la escala de los datos e indicar qué enfoques son factibles. Cuando se enfrentan rangos grandes, valores extremos o cardinalidad alta, es muy probable que los métodos de fuerza bruta, los bucles anidados o los enfoques combinatorios alcancen un límite rápidamente.
Naturalmente, esto es igualmente importante en proyectos de ciencia de datos donde las suposiciones sobre los datos (implícitas o explícitas) pueden generar problemas graves si no se controlan. La investigación temprana de los datos es un paso vital para evitar que los problemas se propaguen hacia abajo, donde pueden resultar mucho más difíciles de solucionar más adelante. La conclusión clave no es evitar en absoluto las suposiciones sobre los datos, sino hacerlas explícitas, documentarlas y probarlas en las primeras etapas del proceso.
Aprendizaje 3: Iterar rápidamente: progreso sobre la perfección
Los acertijos de Advent of Code generalmente se dividen en dos partes. Si bien el segundo a menudo se basa en el primero, introduce una nueva restricción, desafío o giro, como un aumento en el tamaño del problema. El aumento de la complejidad a menudo invalidaba la solución inicial de la primera parte. Sin embargo, esto no significa que la solución a la primera parte sea inútil, ya que proporciona una base valiosa.
Tener una base de referencia de este tipo ayuda a aclarar cómo se comporta el problema, cómo se puede abordar y qué se logra ya con la solución. A partir de ahí, las mejoras pueden abordarse de una manera más estructurada, ya que se sabe qué supuestos ya no se cumplen y qué partes deben cambiar para llegar a una solución exitosa. Por lo tanto, perfeccionar una solución básica concreta es mucho más fácil que diseñar una solución abstracta “perfecta” desde el principio.
En Advent of Code, la segunda parte solo aparece después de que se resuelve la primera, lo que hace que los primeros intentos de encontrar una solución que funcione para ambas partes sean inútiles. Esta estructura refleja una limitación que se encuentra comúnmente en la práctica, ya que normalmente no se conocen todos los requisitos de antemano. Tratar de anticipar de antemano todas las posibles ampliaciones que podrían ser necesarias no sólo es en gran medida especulativo sino también ineficaz.
En la ciencia de datos se pueden observar principios similares. A medida que cambian los requisitos, las fuentes de datos evolucionan y las partes interesadas refinan sus necesidades y solicitudes, los proyectos y soluciones también deben evolucionar. Como tal, comenzar con soluciones simples e iterar basándose en comentarios reales es mucho más efectivo que intentar crear un sistema completamente general desde el principio. Una solución tan “perfecta” rara vez es visible al principio y la iteración es lo que permite que las soluciones converjan hacia algo útil.
Aprendizaje 4: Diseño a escala: conozca los límites
Si bien la iteración enfatiza comenzar con soluciones simples, Advent of Code también señala repetidamente la importancia de comprender la escala y cómo afecta el enfoque que se utilizará. En muchos acertijos, la segunda parte no solo agrega complejidad lógica sino que también aumenta dramáticamente el tamaño del problema. Por lo tanto, una solución con complejidad exponencial o factorial puede ser suficiente para la primera parte, pero empieza a volverse poco práctica cuando el tamaño del problema crece en la segunda parte.
Incluso cuando se comienza con una línea de base simple, es crucial tener una idea aproximada de cómo se ampliará esa solución. Los bucles anidados, la enumeración por fuerza bruta o las búsquedas exhaustivas de combinaciones indican que la solución dejará de funcionar con tanta eficiencia cuando el tamaño del problema crezca. Por lo tanto, conocer el punto de ruptura (aproximado) hace que sea más fácil evaluar si es necesaria una reescritura y cuándo.
Esto no contradice la idea de evitar una optimización prematura. Más bien, indica que uno debe comprender las ventajas y desventajas que implica una solución sin tener que implementar el enfoque más eficiente o escalable de inmediato. Diseñar a escala significa tener conciencia de la escalabilidad y la complejidad, no tener que optimizar a ciegas desde el principio.
Aquí también se establece un paralelo con la ciencia de datos, ya que las soluciones pueden funcionar bien con datos de muestra o conjuntos de datos limitados, pero son propensas a fallar cuando se enfrentan a tamaños de “nivel de producción”. Ser consciente de estos obstáculos, reconocer los posibles límites y tener en cuenta enfoques alternativos hace que estos sistemas sean más resilientes. Saber dónde podría dejar de funcionar una solución puede evitar costosos rediseños y reescrituras posteriores, incluso si no se implementan de inmediato.
Aprendizaje 5: Sea coherente: el impulso supera a la motivación
Una de las conclusiones menos obvias de participar en el Advenimiento del Código tuvo menos que ver con la resolución de problemas y mucho más con “presentarse”. Resolver un rompecabezas todos los días parece manejable en teoría, pero en la práctica fue un desafío, especialmente cuando chocó con la fatiga, el tiempo limitado o una disminución de la motivación, especialmente después de un día completo de trabajo. Por lo tanto, esperar que la motivación reapareciera mágicamente no era una estrategia viable.
El verdadero progreso provino del trabajo diario en los problemas, no de ocasionales estallidos de inspiración. La repetición reforzó formas de pensar y desenmarañar problemas, lo que a su vez generó impulso. Una vez que se generó ese impulso, el progreso comenzó a agravarse y la coherencia importó más que la intensidad.
El desarrollo de habilidades en ciencia de datos rara vez proviene de proyectos únicos o de inmersiones profundas aisladas. Más bien, es el resultado de la práctica repetida, la lectura cuidadosa de los datos, el diseño de soluciones, la iteración de modelos y la depuración de suposiciones realizadas de manera consistente a lo largo del tiempo. Depender de la motivación no es viable, pero tener rutinas fijas lo hace sostenible. Advent of Code ejemplificó esta distinción: mientras la motivación fluctúa, la coherencia aumenta. Tener una estructura diaria de este tipo ayudó a convertir la resolución de acertijos en un hábito en lugar de una aspiración.
Pensamientos finales
Mirando hacia atrás, el valor real que obtuve de participar en Advent of Code no fue resolver acertijos individuales, aprender algunos trucos de codificación nuevos, sino hacer visibles mis hábitos. Destacó dónde tiendo a apresurarme a encontrar soluciones, dónde tiendo a complicarme demasiado y dónde reducir la velocidad y dar un paso atrás me habría ahorrado mucho tiempo. Los acertijos como tales eran solo un medio para un fin, los aprendizajes que obtuve de ellos fueron el valor real.
Advent of Code funcionó mejor para mí cuando lo vi como una práctica deliberada y no como una competencia. Presentarse de manera constante, centrarse en la claridad sobre la inteligencia y refinar las soluciones en lugar de buscar soluciones perfectas desde el principio resultó ser mucho más valioso que encontrar una solución única.
Si aún no lo has probado, te recomiendo que lo pruebes, ya sea durante el evento del próximo año o resolviendo acertijos anteriores. El proceso rápidamente saca a la luz hábitos que se extienden más allá de los propios rompecabezas. Y si te gusta afrontar desafíos, lo más probable es que te resulte una experiencia realmente divertida y gratificante.