Incentivar nuevas compras del cliente en el establecimiento
Fomentar el consumo de otros productos
Fomentar el consumo de productos con más margen de beneficio
In [1]:
import re
from pyknow import *
In [2]:
class Producto(Fact):
"""
Producto que ha comprado un cliente.
>>> Producto(nombre="pepsi", tipo="refresco de cola", cantidad=1)
"""
pass
class Cupon(Fact):
"""
Cupón a generar para la próxima compra del cliente.
>>> Cupon(tipo="2x1", producto="pepsi")
"""
pass
In [3]:
class Promo(Fact):
"""
Promoción vigente en el comercio.
>>> Promo(tipo="2x1", **depende_de_la_promo)
"""
pass
class Beneficio(Fact):
"""
Define los beneficios que obtiene el comercio por cada producto.
>>> Beneficio(nombre="pepsi", tipo="refresco de cola", ganancias=0.2)
"""
pass
In [4]:
class OfertasNxM(KnowledgeEngine):
@DefFacts()
def carga_promociones_nxm(self):
"""
Hechos iniciales.
Genera las promociones vigentes
"""
yield Promo(tipo="2x1", producto="Dodot")
yield Promo(tipo="2x1", producto="Leche Pascual")
yield Promo(tipo="3x2", producto="Pilas AAA")
@Rule(Promo(tipo=MATCH.t & P(lambda t: re.match(r"\d+x\d+", t)),
producto=MATCH.p),
Producto(nombre=MATCH.p))
def oferta_nxm(self, t, p):
"""
Sabemos que el cliente volverá para aprovechar
la promoción, ya que hoy ha comprado el producto.
"""
self.declare(Cupon(tipo=t, producto=p))
In [5]:
watch('RULES', 'FACTS')
In [6]:
nxm = OfertasNxM()
In [7]:
nxm.reset()
In [8]:
nxm.declare(Producto(nombre="Dodot"))
Out[8]:
In [9]:
nxm.declare(Producto(nombre="Agua Mineral"))
Out[9]:
In [10]:
nxm.declare(Producto(nombre="Pilas AAA"))
Out[10]:
In [11]:
nxm.run()
In [13]:
nxm.facts
Out[13]:
In [15]:
class OfertasPACK(KnowledgeEngine):
@DefFacts()
def carga_promociones_pack(self):
"""Genera las promociones vigentes"""
yield Promo(tipo="PACK", producto1="Fregona ACME", producto2="Mopa ACME", descuento="25%")
yield Promo(tipo="PACK", producto1="Pasta Gallo", producto2="Tomate Frito", descuento="10%")
@Rule(Promo(tipo="PACK", producto1=MATCH.p1, producto2=MATCH.p2, descuento=MATCH.d),
OR(
AND(
NOT(Producto(nombre=MATCH.p1)),
Producto(nombre=MATCH.p2)
),
AND(
Producto(nombre=MATCH.p1),
NOT(Producto(nombre=MATCH.p2))
)
)
)
def pack(self, p1, p2, d):
"""
El cliente querrá comprar un producto adicional en su próxima visita.
"""
self.declare(Cupon(tipo="PACK", producto1=p1, producto2=p2, descuento=d))
In [16]:
pack = OfertasPACK()
In [17]:
pack.reset()
In [18]:
pack.declare(Producto(nombre="Tomate Frito"))
Out[18]:
In [19]:
pack.declare(Producto(nombre="Fregona ACME"))
Out[19]:
In [20]:
pack.run()
Si compramos ambos productos de un pack no se nos debe generar la promoción, ya que en este caso el comercio perdería beneficio.
In [21]:
pack.reset()
In [22]:
pack.declare(Producto(nombre="Fregona ACME"))
Out[22]:
In [23]:
pack.declare(Producto(nombre="Mopa ACME"))
Out[23]:
In [24]:
pack.run()
El truco para cumplir este objetivo es conocer qué beneficio se obtiene por cada producto, y si existe un producto del mismo tipo con un beneficio mayor, generar un cupón de descuento para ese producto que nos permita seguir ganando más.
In [26]:
class OfertasDescuento(KnowledgeEngine):
@DefFacts()
def carga_beneficios(self):
"""
Define las beneficios por producto.
"""
yield Beneficio(nombre="Mahou", tipo="Cerveza", ganancias=0.5)
yield Beneficio(nombre="Cerveza Hacendado", tipo="Cerveza", ganancias=0.9)
yield Beneficio(nombre="Pilas AAA Duracell", tipo="Pilas AAA", ganancias=1.5)
yield Beneficio(nombre="Pilas AAA Hacendado", tipo="Pilas AAA", ganancias=2)
@Rule(Producto(nombre=MATCH.p1),
Beneficio(nombre=MATCH.p1, tipo=MATCH.t, ganancias=MATCH.g1),
Beneficio(nombre=MATCH.p2, tipo=MATCH.t, ganancias=MATCH.g2),
TEST(lambda g1, g2: g2 > g1)
)
def descuento_producto_con_mayor_beneficio(self, p2, g1, g2, **_):
"""
"""
diferencia_ganancia = g2 - g1
self.declare(Cupon(tipo="DESCUENTO",
producto=p2,
cantidad=diferencia_ganancia / 2))
In [27]:
descuento = OfertasDescuento()
In [28]:
descuento.reset()
In [29]:
descuento.declare(Producto(nombre="Mahou"))
Out[29]:
In [30]:
descuento.run()
El sistema no debe generar cupón si se ha comprado el producto con mayor beneficio
In [ ]:
descuento.reset()
In [ ]:
descuento.declare(Producto(nombre="Pilas AAA Hacendado"))
In [ ]:
descuento.run()
In [31]:
class GeneradorCupones(OfertasNxM, OfertasPACK, OfertasDescuento):
def generar_cupones(self, *nombre_productos):
# Reiniciamos el motor
self.reset()
# Declaramos los productos que ha comprado el cliente
for nombre in nombre_productos:
self.declare(Producto(nombre=nombre))
# Ejecutamos el motor
self.run()
# Extraemos las promociones generadas
for fact in self.facts.values():
if isinstance(fact, Cupon):
yield fact
In [32]:
ke = GeneradorCupones()
In [33]:
[cupon for cupon in ke.generar_cupones("Pilas AAA", "Mahou", "Tomate Frito")]
Out[33]:
In [ ]: