Muhammad Grandiv Lava Putra (22/493242/TK/54023) Muhamad Farrel Adrian (22/505897/TK/55394)
Retail stores face numerous challenges in optimizing their operations, especially from the aspect of the store layout. The objective of this project is to optimize retail store operations to increase sales by analyzing how different store layouts affect active customer numbers, needed items acquisition, and the time customers spend in the store. Specifically, we aim to determine the optimal store layout to maximize sales by strategically placing high-demand items, thereby improving customer flow and reducing the time customers spend locating and purchasing items.
The simulation and modeling approach is based on retail analytics theory, which emphasizes that store layout significantly influences customer behavior, including the time spent in-store and item acquisition patterns. According to Larso, Bradlow, and Fader’s (2005) work on “An Exploratory Look at Supermarket Shopping Paths”, optimizing customer paths and the placement of high-demand items can enhance overall store performance.
The simulation and modeling approach utilized in this study is grounded in retail analytics theory, emphasizing that store layout significantly impacts customer behavior, including time spent in-store and item acquisition patterns. According to Larson, Bradlow, and Fader (2005), optimizing customer paths and strategically placing high-demand items can enhance overall store performance. Agent-Based Modeling (ABM) is particularly well-suited for such simulations, as it captures the interactions between individual agents (customers) and their environment, leading to emergent behaviors that reflect real-world complexities. ABM allows for the modeling of heterogeneous agent behaviors and their adaptation over time, providing a nuanced understanding of how different store layouts affect customer actions and store outcomes (Bonabeau, 2002; Macal & North, 2010).
The MESA library, a Python-based ABM framework, offers tools for building, visualizing, and analyzing these complex simulations. It supports customization and extensibility, making it possible to simulate various retail scenarios effectively (Kazil, 2020). The simulation results highlight the effectiveness of different store layouts: the horizontal layout against the entrance (Scenario 1) was found to be the most optimal, maximizing customer engagement and exploration time. This aligns with theories suggesting that visible, accessible layouts encourage longer in-store times and increased item discovery, thus enhancing overall store performance (Hui, Fader, & Bradlow, 2009).
Overall, the integration of ABM and retail analytics through the MESA library provides a powerful approach to optimizing store layouts. The findings underscore the importance of strategic shelf placement and layout design in influencing customer behavior and store efficiency. By simulating various scenarios, retailers can better understand how to design spaces that maximize customer satisfaction and operational success (Larson, Bradlow, & Fader, 2005; Sorensen, 2009; Thaler & Sunstein, 2008).
It models customer behavior, focusing on how customers interact with the store environment. Each client is initialized with a specific position in the store, a list of items to collect, and a decision engine to guide their actions. The client’s primary attributes include their current position, items needed, items collected, current action, past actions, and the total time spent in the store.
Clients operate by evaluating their surroundings, deciding on movements, and performing actions such as picking items from shelves or checking out at exits. They utilize a decision engine to make movement choices and determine their next steps based on the store layout and their remaining needs. If they still have items to be picked, they will roam the store. The relative weights of each category also affects the client behavior. They will be more likely to go to the category which has higher relative weights. If the items they needed are zero, they will navigate to one of the exit doors. Hence, the lesser the exit doors, the higher chance of the clients to spend more time browsing through the shelves in the store.
Throughout the simulation, clients update their state, record their actions, and eventually leave the store upon completing their shopping list.
class Client(Agent):
def __init__(self, pos, items_to_get, model):
super().__init__(pos, model)
self.mind = DecisionEngine(self)
self.x, self.y = pos
self.pos = pos
self.need = items_to_get
self.have = Counter()
self.action = None
self.past_actions = []
self.time_total = 0
def display(self):
return {
"Shape": "rect",
"text": self.need_count(),
"w": 1,
"h": 1,
"Filled": "true",
"Layer": 0,
"x": self.x,
"y": self.y,
"Color": "white" if not self.action else "yellow",
"text_color": "red"
}
@property
def neighbors(self):
return self.model.grid.iter_neighbors((self.x, self.y),
True)
@property
def surround(self):
return self.model.grid.iter_neighbors((self.x, self.y),
moore=True, radius=15)
def done(self):
for n in self.need.most_common():
if n[1] > 0:
return False
return True
def need_count(self):
total = 0
for n in self.need.most_common():
total += n[1]
return total
def step(self):
pass
def advance(self):
self.time_total += 1
if self.action:
self.action.step()
elif not self.using_shelves() and not self.using_exits():
moves = [pos for pos in self._possible_moves()
if self.model.grid.is_cell_empty(pos)] + [self.pos]
next_pos = self.mind.make_decision(self.pos, moves)
assert (next_pos in moves)
if next_pos is not self.pos:
self.model.grid.move_agent(self, next_pos)
self.x, self.y = self.pos
def using_shelves(self):
if not self.done() and not self.action:
for n in self.neighbors:
if hasattr(n, "category") and n.category
in self.need and self.need[n.category] > 0:
self.action = PickAction(self, n)
return True
return False
def using_exits(self):
if self.done() and not self.action:
for n in self.neighbors:
if hasattr(n, "check_out"):
self.action = CheckOutAction(self)
return True
return False
def remove(self):
self.model.schedule.remove(self)
self.model.grid.remove_agent(self)
self.model.total_time_collector.commit_time
(self.time_total, self.past_actions)
def _possible_moves(self):
return moveUtils.places_to_move(self.x, self.y,
self.model.width, self.model.height)