Leia nosso artigo sobre

class="lazyload

A otimização do sortimento é um processo fundamental no varejo que envolvea seleção da combinação ideal de produtospara atender à demanda dos consumidores, levando em conta as diversas restrições logísticas envolvidas. Os varejistas precisam garantir que oferecem os produtos certos, nas quantidades certas e no momento certo. Ao aproveitar data os insights sobre os consumidores, os varejistas podem tomar decisões informadas sobre quais itens estocar, como gerenciar o estoque e quais produtos priorizar com base nas preferências dos clientes, nas tendências sazonais e nos padrões de vendas.

Para as empresas do setor de varejo, a otimização do sortimento é essencial para encontrar um equilíbrio entrevariedadeeeficiência. Oferecer poucas opções pode afastar os clientes, enquanto oferecer muitas pode causar confusão, excesso de estoque e margens de lucro mais baixas. Otimizar o sortimento de produtos ajuda as empresas a aumentar a satisfação do cliente, garantindo a disponibilidade de itens populares e eliminando produtos de baixo desempenho que ocupam espaço valioso nas prateleiras.

A modelagem de escolhaé uma forma eficiente de abordar a otimização do sortimento, pois oferece uma estrutura data para compreender as preferências dos clientes e prever como eles escolherão entre diferentes produtos. Ao analisar vários fatores, como sensibilidade ao preço, características do produto e fidelidade à marca, a modelagem de escolha ajuda os varejistas a identificar quais produtos têm maior probabilidade de atender à demanda dos clientes.

Em última análise, a modelagem de escolha permite que os varejistas ofereçam o mix certo de produtos, adaptem os sortimentos a segmentos específicos de clientes e também possam otimizar o espaço nas prateleiras para aumentar a rentabilidade ou até mesmo definir os preços dos itens.

Se você nunca ouviu falar em modelagem de escolha, pode lernosso artigoque apresenta os conceitos-chave com exemplos. Neste artigo, vamos nos concentrar principalmente em como os modelos de escolha discreta podem ser usados para otimizar um sortimento de produtos. Fornecemos exemplos de código baseados na bibliotecachoice-learn, que foi projetada para ajudar data nesses casos de uso.

O código fornecido utiliza o pacote Python choice-learn e pode ser encontrado em um notebook aqui.

Configuração: Instalando o Python e o Choice-Learn

Neste artigo, apresentamos trechos de código para ilustrar as explicações. O código utiliza a bibliotecaChoice-Learn, que oferece ferramentas eficientes para modelagem de escolhas e diversas aplicações — como otimização de sortimento ou preços. A Choice-Learn está disponível no PyPI; você pode baixá-la facilmente com


pip install choice-learn

O conjunto de dados: notas fiscais

Usaremos o conjunto de dados TaFeng Grocery. Você pode baixá-lo doKagglee abri-lo no seu ambiente Python com o choice-learn:

importar load_tafeng de choice_learn.datasets
tafeng_df = load_tafeng(as_frame=True)
print(tafeng_df.head())
class="lazyload

O conjunto de dados é composto por mais de 800.000 compras individuais realizadas em um supermercado chinês. Para cada compra, são fornecidos vários detalhes, incluindo o item adquirido (PRODUCT_ID), o preço pelo qual foi vendido (SALES_PRICE) e a faixa etária do cliente (AGE_GROUP).

É possível observar que são oferecidos muitos itens diferentes, e alguns deles raramente são vendidos. Para otimizar a logística, o varejista pode optar por reduzir o número de produtos que oferece. O objetivo, nesse caso, é identificar o subconjunto ideal de itens a serem vendidos.

Para isso, nos concentramos nos itens mais vendidos, pois é mais provável que sejam comprados novamente e desempenharão um papel crucial na formação de um sortimento mais eficiente e lucrativo.Observe que fazemos isso principalmente para simplificar o exemplo e que todos os itens poderiam ser mantidos.

# Manter apenas os 20 produtos mais vendidos
tafeng_df = tafeng_df.loc[
tafeng_df.PRODUCT_ID.isin(tafeng_df.PRODUCT_ID.value_counts().index[:20])
].reset_index(drop=True)
# Remover valores NaN
tafeng_df = tafeng_df.loc[
tafeng_df.AGE_GROUP.isin([“25-29”, “40-44”, “45-49”, “>65”, “30-34”, “35-39”, “50-54”, “55-59”, “60-64”] )
].reset_index(drop=True)
print(tafeng_df.head())

Vamos também codificar as faixas etárias com valores “one-hot” a cada dez anos:

# Codificação das faixas etárias
tafeng_df[“twenties”] = tafeng_df.apply(lambda row: 1 if row[“AGE_GROUP”] == “25-29” else 0,axis=1)
tafeng_df[“thirties”] = tafeng_df.apply(
lambda row: 1 if row[“AGE_GROUP”] in ([“30-34”, “35-39”]) else 0,axis=1
)
tafeng_df[“forties”] = tafeng_df.apply(
lambda row: 1 if row[“AGE_GROUP”] in ([“40-44”, “45-49”]) else 0,axis=1
)
tafeng_df[“fifties”] = tafeng_df.apply(
lambda row: 1 if row[“AGE_GROUP”] in ([“50-54”, “55-59”]) else 0,axis=1
)
tafeng_df[“sixties_and_above”] = tafeng_df.apply(
lambda row: 1 if row[“AGE_GROUP”] in ([“60-64”, “>65”]) else 0,axis=1
)

Agora que nossos data prontos, precisamos criar umChoiceDataset, o objeto data dochoice-learn. Isso envolve especificar as características que descrevem o contexto em que uma compra é realizada:

  • Características dos clientes(características comuns): a faixa etária
  • Características do produto(detalhes do item): o preço do item

Um aspecto fundamental da modelagem de escolha é que precisamos das características detodos os itens disponíveis no momento da compra, e não apenas do item escolhido. Isso nos permite analisar como os preços dos diferentes produtos influenciam a decisão do cliente. Como essa informação não está diretamente disponível no conjunto de dados, partimos do pressuposto de que, para cada compra, os preços dos outros itens permanecem os mesmos da venda anterior.

# ID do produto para o índice
id_to_index =
for i, product_id in enumerate(np.sort(tafeng_df.PRODUCT_ID.unique())):
id_to_index[product_id] = i
# Inicializar o preço dos itens
prices = [[0] for _ in range(len(id_to_index))] for k, v in id_to_index.items():
prices[v][0] = tafeng_df.loc[tafeng_df.PRODUCT_ID == k].SALES_PRICE.to_numpy()[0] # Criar as matrizes que constituirão o ChoiceDataset
shared_features = [] items_features = [] choices = [] # Para cada item comprado, salvamos:
# – a representação da idade (one-hot) do cliente
# – o preço de todos os itens vendidos
for i, row in tafeng_df.iterrows():
item_index = id_to_index[row.PRODUCT_ID] prices[item_index][0] = row.SALES_PRICE
shared_features.append(
row[["twenties", "thirties", "forties", "fifties", "sixties_and_above"]].to_numpy()
)
items_features.append(prices)
choices.append(item_index)

Agora que temos todas as informações, podemoscriar o ChoiceDataset:

fromdata import ChoiceDataset
dataset = ChoiceDataset(
shared_features_by_choice=shared_features,
shared_features_by_choice_names=[‘twenties’, ‘thirties’, ‘forties’, ‘fifties’, ‘sixties_and_above’],
itens_características_por_escolha=itens_características,
itens_características_por_escolha_nomes=[“PREÇO_DE_VENDA”],
escolhas=escolhas
)

Definição e estimativa do modelo de escolha

Desenvolveremos e estimaremos um modelo de escolha que preveja a probabilidade de um cliente selecionar um item específico de um conjunto completo de produtos semelhantes. Com base no conjunto de dados disponível, definimos a seguinte função de utilidade para um itemiconsiderado por um clientej:

class="lazyload

Essa função representa a utilidade (ou satisfação) que um cliente obtém ao escolher um determinado item, influenciada tanto pela idade do cliente quanto pelo preço do item.

Para mais detalhes sobre como formulamos uma função de utilidade, consulte nossa primeirapublicação. Observe que outro modelo lógico — mas que não foi apresentado para simplificar — poderia consistir em estimar uma sensibilidade de preço por faixa etária.

Aqui está o código para estimar esse modelo com o `choice-learn`:

importar ConditionalLogit de choice_learn.models
model = ConditionalLogit(optimizer="Adam",batch_size=1024,epochs=300,lr=0.002)
para age_category em [“20 anos”, “30 anos”, “40 anos”, “50 anos”, “60 anos e mais”]:
model.add_coefficients(
coefficient_name=age_category, feature_name=age_category,items_indexes=list(range(20))
)
model.add_shared_coefficient(
coefficient_name="price", feature_name="SALES_PRICE",items_indexes=list(range(20))
)
hist = model.fit(dataset)

Você pode verificar se o modelo se ajusta bem ao conjunto de dados:

import matplotlib.pyplot as plt
plt.plot(hist[“train_loss”])
plt.xlabel(“Época”)
plt.ylabel(“Logaritmo negativo da verossimilhança”)
plt.show(
class="lazyload

Encontrar o sortimento ideal

Com as probabilidades de compra em mãos, podemos agora estimar a receita média por cliente de um sortimentoA usandoa fórmula:

class="lazyload

Para encontrar o sortimento que maximize a receita, poderíamos avaliar todas as combinações possíveis e selecionar aquela com a maior receita média. No entanto, uma abordagem mais eficiente é utilizara Programação Linear (PL). Aqui, vamos nos concentrar em como usar a implementaçãochoice-learndo otimizador de sortimento.

É importante distinguir entre maximizar a receita e maximizar as margens de lucro. Embora a receita seja importante, as margens de lucro levam em conta os custos associados a cada produto. Dependendo do seu objetivo, talvez seja melhor otimizar com foco no lucro, em vez de apenas na receita.

Para otimizar o sortimento, precisamos fornecer várias informações essenciais:

  • A ponderação que queremos atribuir a cada faixa etária; vamos usar a participação de mercado de cada uma delas
  • A utilidade de cada item (calculada pelo nosso modelo de escolha) para cada faixa etária
  • O valor a ser otimizado para cada item (neste caso, a receita)
  • O tamanho do sortimento (por exemplo, 12 itens)

Veja como funciona como choice-learn:


from choice_learn.toolbox.assortment_optimizer import LatentClassAssortmentOptimizer
# Preço de cada item
future_prices = np.stack([items_features[-1]]*5,axis=0)
age_category = np.eye(5).astype("float32")
# Calcular a utilidade de cada item, considerando seu preço e cada categoria de idade
predicted_utilities = model.compute_batch_utility(shared_features_by_choice=age_category,
                                                  items_features_by_choice=future_prices,
                                                  available_items_by_choice=np.ones((5, 20)),
                                                 choices=None
                                                  )
age_category_weights = np.sum(shared_features,axis=0) / len(shared_features)
opt = LatentClassAssortmentOptimizer(
solver="or-tools", # Solucionador a ser usado, seja "or-tools" ou "gurobi" (se você tiver uma licença)
class_weights=age_category_weights, # Pesos de cada classe
class_utilities=np.exp(predicted_utilities), # utilidades na forma (n_classes, n_items)
itemwise_values=future_prices[0][:, 0], # Valores a serem otimizados para cada item; aqui, o preço usado para calcular o faturamento
assortment_size=12) # Tamanho do sortimento desejado
assortment, opt_obj = opt.solve()

Ao executar o código, você deverá obter algo como:


class="lazyload

O sortimento ideal para maximizar a receita é indicado pelos índices iguais a 1 no vetor. Esse sortimento gera, teoricamente, uma receita média por cliente de 134 yuans. Você pode explorar outras combinações, mas todas elas resultarão em uma receita média menor.

Outro objetivo poderia ser maximizar o número de vendas. Nesse cenário, o valor de otimização por item é definido como 1 para todos os itens, resultando em um sortimento ótimo diferente.

A eficácia desse método torna-se evidente quando são introduzidas restrições adicionais. Por exemplo, talvez seja necessário levar em conta as limitações de espaço nas prateleiras da sua loja. Nesse caso, é possível otimizar o sortimento de forma que o volume total dos itens não exceda o espaço disponível nas prateleiras. Essa restrição adicional, juntamente com outras, como estratégias de preços, é demonstradaaqui.


Conclusão

Se você está trabalhando com otimização de sortimento ou preços, a modelagem de escolha é uma ótima ferramenta; não deixe de dar uma olhada. O Choice-Learn oferece vários exemplos interessantes noGitHub. Dê uma olhada e marque com uma estrela se achar útil!