Bagels – Adivina el número | Python

En esta entrada vamos a revisar la implementación del juego Bagels en Python. Este juego consiste en adivinar un numero de 3 dígitos.

Como funciona:

Bagels es un juego de deducción que puedes jugar con un amigo. Tu amigo piensa en un número aleatorio de 3 dígitos sin dígitos repetidos y tú intentas adivinar cuál es el número. Después de cada intento, tu amigo te da tres tipos de pistas:

  • Bagels: Ninguno de los tres dígitos que adivinó está en el número secreto.
  • Pico: Uno de los dígitos está en el número secreto, pero su conjetura tiene el dígito en el lugar equivocado.
  • Fermi: Tu conjetura tiene un dígito correcto en el lugar correcto.

Nota: En matemáticas, el concepto de conjetura se refiere a una afirmación o una proposición que se supone cierta, pero que no ha sido demostrada ni refutada hasta la fecha.​​​​

Ahora vamos con la implementación del código:

import random

NUM_DIGITS = 3 # Numero de digitos del número a adivinar
MAX_GUESSES = 10 # Número de intentos

def main():
    print('''Implementación del juego bagels
        Estoy pensando en un número de {} digitos, cuyos digitos no se repiten
        Cuando diga:
            Pico    Uno de los dígitos está en el número secreto, pero su conjetura tiene el dígito en el lugar equivocado.
            Fermi   Tu conjetura tiene un dígito correcto en el lugar correcto.
            Bagels  Ninguno de los tres dígitos que adivinó está en el número secreto.
                    
    '''.format(NUM_DIGITS))
    
    while True: # Bucle principal del juego
        #Almacenar el número secreto generado de manera aleatoria
        secretNum = getSecretNum()                        
        print('He pensado un número.')
        print('Tienes {} intentos para conseguirlo.'.format(MAX_GUESSES))
        
        numGuesses = 1
        while numGuesses <= MAX_GUESSES:
            guess = ''
            #Se mantiene el bucle hasta que se ingrese una conjetura valida
            while len(guess) != NUM_DIGITS or not guess.isdecimal():
                print('Conjetura #{}: '.format(numGuesses))
                #Tomar la conjetura ingresada por el usuario
                guess = input('> ')
                
                #Obtenemos las pistas
                clues = getClues(guess, secretNum)
                print(clues)
                numGuesses += 1
                
            if guess == secretNum:
                break #Si es correctos sale del bucle
            
            if numGuesses > MAX_GUESSES:
                print('Número de conjeturas consumidas')
                print('La respuesta era {}.'.format(secretNum))
        
        #Preguntar si el jugador quiere volver a jugar
        print('Deseas volver a jugar? (si o no)')
        if not input('> ').lower().startswith('s'):
            break
        
    print ('Gracias por jugar')

def getSecretNum():
    """Devuelme una cadena formada por un número aleatorio de digitos"""
    numbers = list('0123456789') #Creamos una lista de digitos del 0 al 9
    random.shuffle(numbers) #Barajamos la lista en orden aleatorio
    
    #Obtenemos los primeros números de la lista que seran utilizados para el juego
    secretNum = ''
    for i in range(NUM_DIGITS):
        secretNum += str(numbers[i])
        
    return secretNum
    

def getClues(guess, secretNum):
    """ Devuelve una cadena con las pistas: Pico, Fermi y Bagels """
    if guess == secretNum:
        return "Acertaste!"
    
    clues = []
    
    for i in range(len(guess)):
        if guess[i] == secretNum[i]:
            #Una conjetura correcta en el lugar correcto
            clues.append('Fermi')
            print(clues)
        elif guess[i] in secretNum:
            #Una conjetura correcta en el lugar incorrecto
            clues.append('Pico')
            print(clues)
    
    #No existe ningúna conjetura correcta
    if len(clues) == 0:
        return 'Bagels' 
    else:
        #Clasifica las pistas en orden alfabetico para que su orden original no revele información
        clues.sort()
        #Regresamos una sola cadena
        return ' '.join(clues)

#Ejecutar el juego
if __name__=='__main__':
    main()

https://amzn.to/48ggISV

¿Cuáles son los beneficios de usar Python?

Python es uno de los lenguajes más poderosos usado incluso por empresas como Google. Entre los beneficios podemos encontrar los siguientes:

  • Eficiencia: Python es muy eficiente en el uso de memoria. Útil para programas que usen grandes conjuntos de datos como Dig Data.
  • Más Rápido: A pesar de que en Python el código es interpretado, es conocido por su alto rendimiento en cuanto a velocidad.
  • Ampliamente usado: Es utilizado en diferentes organizaciones para diferentes proyectos. Debido a esta amplitud puedes encontrar miles de complementos.
  • Fácil de aprender: Es un lenguaje muy fácil de aprender. Esto es algo de lo más reconocido para Python. Tareas que podrían parecer complejas son más fáciles de implementar en Python.

Pilas en Python | Stack in Python

Una pila es una estructura de datos lineal que almacena los elementos en modo Last In First Out (LIFO). En la pila se puede agregar un nuevo elemento (PUSH) o remover un elemento (POP), el elemento agregado o removido siempre corresponde al elemento de la cima (TOP) de la pila.

Para una pila vacía el valor del elemento TOP es -1. Cuando se inicializa una pila configuramos este valor en -1 de tal manera que podamos verificar si la pila esta vacía.

Operaciones en las pilas

  1. Operación de apilamiento (PUSH): Agrega un nuevo elemento a la cima de la pila. En esta operación se debe verificar si la pila aún no se ha llenado
  2. Operación de desapilar (POP): Remueve el elemento de la cima de la pila. En esta operación se debe verifica si a pila no esta vacía.
  3. Consultar (PEEK or TOP): Retorna el valor de la cima de la pila.
  4. estaVacía (isEmpty): Verifica si la pila esta vacía.
  5. estaLlena (isFull): Verifica si la pila esta llena.

Desbordamiento y subdesbordamiento de la pila

  • Desbordamiento (Overflow): Si la pila esta llena, entonces se dice que la pila esta desbordada. En otras palabra, esto ocurre cuando tratamos de agregar un nuevo elemento cuando se ha superado el máximo permitido.
  • Subdesbordamiento (Underflow): Si la pila esta vacía decimo que está subdesbordada. En otras palabras, esto ocurre cuando tratamos de remover un elemento cuando no hay elementos (TOP = -1).

Implementando una pila

"""

Timosoft: 22/02/2023

"""

class Stack:    
    def __init__(self, size):
        #inializamos la pila  TOP=-1
        self.stack = []
        self.maxSize = size
        self.top = -1
        
    #Apilar elementos
    def push(self, x):
        if self.top == self.maxSize - 1:
            print('Stack Overflow')
        else:
            self.x = x
            self.stack.append(x)
            self.top += 1
    
    #Desapilar/quitar elementos
    def pop(self):
        if self.top == -1:
            print('Stack underflow')
        else:
            self.stack.pop()
            self.top -= 1
    
    def isEmpty(self):
        print(self.top == -1)
        
    def isFull(self):
        print(self.top == self.maxSize-1)
    
    def peek(self):
        print(self.stack[-1])
    
    def displayElements(self):
        #range(start, stop, step)
        for i in range(self.top, -1, -1):
            print(self.stack[i])

#Declaramos una pila de 3 elementos            
stackObj = Stack(3)
#Agreamos 3 elementos
stackObj.push(1)
stackObj.push(2)
stackObj.push(3)
#Mostramos los elementos
stackObj.displayElements()
#Verificamos si la pila esta llena
stackObj.isFull()
#Removemos un elemento
stackObj.pop()
#Mostramos los elementos
stackObj.displayElements()
#Verificamos si la pila esta llena
stackObj.isEmpty()
#Consultamos el elemento de la cima
stackObj.peek()

Resultado:

3
2
1
True
2
1
False
2

Espero que te sirva este ejemplo, no olvides visitar nuestro contenido.

Sentencias SQL Básicas en Oracle

En la siguiente tabla presentamos consultas básicas SQL en Oracle.

DescripciónEstructura BásicaEjemplo
Comando para visualizar todas
las columnas de la tabla
SELECT *
FROM TABLE;
SELECT *
FROM libros;
Comando para visualizar una
columna de la tabla
SELECT nombrecolumna
FROM TABLE;
SELECT titulo
FROM libros;
Comando para visualizar múltiples
columnas de la tabla
SELECT nombrecolumna1,
nombrecolumna2, …
FROM TABLE;
SELECT titulo, autor
FROM libros;
Comando para asignar una alias
a una columna al mostrar el resultado
SELECT nombrecolumna
[AS] alias
FROM TABLE;
SELECT titulo AS titulos
FROM libros
O
SELECT titulo titulos
FROM libros;
Comando para utilizar expresiones
aritméticas durante la obtención
SELECT expresión aritmética
FROM TABLE;
SELECT costo-descuento
FROM libros;
Comando para eliminar duplicidad
en la salida
SELECT DISTINCT nombrecolumna
FROM TABLE;
O
SELECT UNIQUE nombrecolumna
FROM TABLE;
SELECT DISTINCT autor
FROM libros;
O
SELECT UNIQUE autor
FROM libros;
Comando para concatenar una columna
al mostrar el resultado
SELECT nombrecolumna1,
|| nombrecolumna2
FROM TABLE;
SELECT nombre || ‘ ‘ || apellido
FROM libros;
Comando para visualizar la estructura
de una tabla
DESCRIBE TABLE:DESCRIBE libros;
Consultas comunes el SQL – Oracle

Creando un accediendo a un Pandas DataFrame

En esta entrada vamos a empezar a familiarizarnos con Pandas, si aún no conoces claramente que es Pandas puedes visitar mi entrada anterior ¿Qué es Pandas?. Estaremos revisando la creación básica de un DataFrame con un dicciocionario, acceder al DataFrame mediante el formato de diccionario y por los métodos iloc y loc.

Creación y acceso a un DataFrame.

Pandas tiene la sintaxis similar a un diccionario, cada nombre de columna se trata como una clave,
y los valores de fila se devuelven como el valor.

Ahora veamos como crear un DataFrame a partir de un diccionario.

  1. Importar la biblioteca pandas
  2. Crear el DataFrame: Hacer uso de la función DataFrame pasando como argumento el diccionario
  3. Mostrar el DataFrame: Se ingresa el nombre dado al DataFrame
  4. Acceder al DataFrame y mostrar la serie «nombre»: Colocar el nombre del DataFrame y entre corchetes el nombre de la columna.
>> import pandas as pd

>> cuentas = pd.DataFrame({
    "nombre": ["Edwin", "María", "Rosario"],
    "cuenta": [123456, 123457, 123458],
    "balance": [120, 240, 320],
})

>> cuentas["nombre"]
0      Edwin
1      María
2    Rosario
Name: nombre, dtype: object

Tener en cuenta que al obtener la serie o columna «nombre» se apunta al DataFrame original , esto nos permite modificar el original fácilmente, ayudando al rendimiento basado en memoria ya que no estamos constantemente creando copias.

>> cuentas["nombre"] = ["Timo","Tarja","Janis"]

>> cuentas
  nombre  cuenta  balance
0   Timo  123456      120
1  Tarja  123457      240
2  Janis  123458      320

Como puedes apreciar al volver a mostrar el DataFrame cuentas se han actualizado los nombres.

Creando un sub-DataFrame

Para crear un sub-DataFrame pasamos una lista con el nombre de las columnas.

>> diccCuentas = {
    "nombre": ["Edwin", "María", "Rosario"],
    "cuenta": [123456, 123457, 123458],
    "balance": [120, 240, 320],
}

>> cuentas = pd.DataFrame(diccCuentas)

>> cuentas
    nombre  cuenta  balance
0    Edwin  123456      120
1    María  123457      240
2  Rosario  123458      320

>> cuentas[["nombre", "balance"]]

    nombre  balance
0    Edwin      120
1    María      240
2  Rosario      320
Como puedes ver le pasamos la lista ["nombre", "balance"] para mostrar el sub-DataFrame.
Hasta ahora hemos estado utilizado la sintaxis de diccionario para acceder al DataFrame, pero la mejor forma de hacer esto es mediante iloc. 

El Método iloc

Para acceder a las filas de un DataFrame hacemos uso del método iloc. Recordar que el número de filas empieza en 0.

>> cuentas.iloc[1]
nombre      María
cuenta     123457
balance       240
Name: 1, dtype: object

Con esto imprimimos la fila número 1 correspondiente a María.

Podemos usar también slicing en el método iloc.

>> cuentas.iloc[0:2]
  nombre  cuenta  balance
0  Edwin  123456      120
1  María  123457      240

>> cuentas.iloc[:]
    nombre  cuenta  balance
0    Edwin  123456      120
1    María  123457      240
2  Rosario  123458      320

iloc se utiliza para indexar un DataFrame a través de la indexación basada en posiciones enteras.
La primera posición en la función iloc especifica los índices de fila, mientras que la segunda posición especifica los índices de las columnas. Esto significa que podemos seleccionar filas y columnas.

cuentas

    nombre  cuenta  balance
0    Edwin  123456      120
1    María  123457      240
2  Rosario  123458      320

cuentas.iloc[1,1]
Out[56]: 123457

En este caso hemos impreso el valor de la fila 1 y columna 1.

>> cuentas.iloc[0,2]
120

Podemos de igual manera combinar con slicing para filtrar filas y columnas.

>> cuentas.iloc[:,[0,2]]
    nombre  balance
0    Edwin      120
1    María      240
2  Rosario      320

En este caso hemos impreso todas las filas, y las columnas 0 y 2.

>> cuentas.iloc[2:,[0,2]]
    nombre  balance
2  Rosario      320

iloc tambien permite matrices booleanas. Por ejemplo si queremos mostrar los índices de las filas pares.

>> cuentas.iloc[cuentas.index %2 == 0]

    nombre  cuenta  balance
0    Edwin  123456      120
2  Rosario  123458      320

Cuando tienes multi-índices puedes también usar iloc para filtrar por el nombre de las columnas. Para esto vamos a usar columns.get_level_values(indice).

diccMusicos ={
    "nombre" : ["Kurt Cobain", "Dave Mustaine", "James Hetfield", "Slash"],
    "nacimiento": ["20-02-1967", "13-08-1961", "03-08-1963", "23-07-1965"],
    "genero": ["Grunge", "Thrash Metal", "Thrash Metal", "Hard Rock"],
    "nombre banda": ["Nirvana", "Megadeth", "Metallica", "Guns N Roses"],
    }

musicos = pd.DataFrame(diccMusicos)

musicos_columna = musicos.columns.get_level_values(0).isin({"nombre", "genero"})

musicos.iloc[:, musicos_columna]
Out[115]: 
           nombre        genero
0     Kurt Cobain        Grunge
1   Dave Mustaine  Thrash Metal
2  James Hetfield  Thrash Metal
3           Slash     Hard Rock

El método loc

Este método es similar a iloc, pero permite indexar el DataFrame por el nombre de las columnas o su etiqueta.

En el siguiente ejemplo accedemos a una fila y columna.

>> import pandas as pd

>> cuentas = pd.DataFrame({
    "nombre": ["Edwin", "María", "Rosario"],
    "cuenta": [123456, 123457, 123458],
    "balance": [120, 240, 320],
})

>> cuentas.loc[0, "cuenta"]
123456

En el siguiente ejemplo, obtenemos todas las filas y las columnas cuenta y balance, recuerda que al consultar más de una columna debes usar una lista.

>> cuentas.loc[:, ["cuenta", "balance"]]

   cuenta  balance
0  123456      120
1  123457      240
2  123458      320

Filtrar la fila y columna de un DataFrame multinivel.

>> df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo",
                         "bar", "bar", "bar", "bar"],
                   "B": ["one", "one", "one", "two", "two",
                         "one", "one", "two", "two"],
                   "C": ["small", "large", "large", "small",
                         "small", "large", "small", "small",
                         "large"],
                   "D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
                   "E": [2, 4, 5, 5, 6, 6, 8, 9, 9]})

>> table = pd.pivot_table(df, values='D', index=['A', 'B'],
                    columns=['C'], aggfunc=np.sum)

table

C        large  small
A   B                
bar one    4.0    5.0
    two    7.0    6.0
foo one    4.0    1.0
    two    NaN    6.0

>> table.loc['bar','large']
B
one    4.0
two    7.0
Name: large, dtype: float64

En el ejemplo anterior filtramos el primer pedazo de A que viene a ser bar , pero si queremos acceder a B, debemos usar slice. Debes usar un slice por cada parte de la fila que vamos a considerar.

>> table.loc[(slice(None), slice('one')), ['large', 'small']]

C        large  small
A   B                
bar one    4.0    5.0
foo one    4.0    1.0

¿Qué es Pandas? | Python

Pandas proviene del termino Panel Data. Estos paneles de datos hacen referencia a datos tabulares, la idea es hacer paneles a partir de paneles más grandes.

Al inicio de Pandas estuvo muy relacionado con NumPy como un proyecto de código abierto de mano de su creador Wes McKinney.

Pandas es una biblioteca de análisis de datos de Python, algunas fuentes la llaman la hoja de calculo basada en Python con súper poderes. Para las personas que utilizan Excel es una buena opción ya que hace que tareas engorrosas sean más fáciles, rápidas y menos propensas a errores. Algunas de estas tareas incluyen obtener grandes conjuntos de datos de fuentes externas y trabajar con estadísticas, series temporales y gráficos interactivos. Los súper poderes más importantes de los pandas son la vectorización y la alineación de datos. La vectorización le permite escribir código conciso basado en matrices, mientras que la alineación de datos garantiza que no haya discrepancias en los datos cuando trabaja con varios conjuntos de datos.

Generalmente trabaja con conjuntos de datos inferiores a 1GB, pero esto puede variar dependiendo de la memoria del dispositivo en que se ejecuta. Es una buena regla tener una memoria entre cinco y diez veces superior al tamaño del conjunto de datos. En caso de que el tamaño del conjunto de datos ya supere el un digito es recomendable usar una biblioteca diferente como Vaex.

Este módulo se ha vuelto muy popular entre los analistas de datos y usuarios de Python, ya que proporciona muchas funciones para leer/escribir, combinar, transformar y administrar datos. También tiene funciones para calcular estadísticas y crear diagramas. Estas funciones simplifican y reducen la cantidad de código a escribir.

Pandas esta construido alrededor de estructuras de datos centrales llamadas DataFrame y Series. Los componentes principales de un DataFrame son: índice, columnas y datos. Un DataFrame es similar a una matriz NumPy bidimensional, pero viene con etiquetas de columna y fila y cada columna puede contener diferentes tipos de datos. Al extraer una sola columna o fila de un DataFrame, se obtiene una Serie unidimensional. Nuevamente, una serie es similar a una matriz NumPy unidimensional con etiquetas.

Con el siguiente gráfico lo podrás comprender facilmente.

Puedes ver también la similitud del DataFrame con una hoja de Excel.

Antes de finalizar indicar que Pandas permite consultas tipo SQL y uniones de tablas, así como su capacidad de ingesta de gran variedad de formatos de archivo y base de datos, como SQL, archivo de Excel y archivos separados por comas (CSV).

En las próximas entradas, se revisara ya el código para crear y manipular los DataFrame.

Bibliografía:

Te recomiendo los siguientes libros para que profundices en pandas.

Thinking in Pandas: How to Use the Python Data Analysis Library the Right Way

Python for Excel

Introduction to Machine Learning with Python