Want to get involved? We're always looking for ideas and content for Weekly Challenges.
SUBMIT YOUR IDEAThis was a great challenge - a simple problem to practice an infrequently-used tool. I have to spend quality time with Tool Mastery and help for some of these!
I solved this one with the Optimization tool (variables in rows) and with Python (PuLP). The output from the Python method is valid but slightly different (57 white daisies / 40 red roses, while the Optimization tool produced 40 white daisies / 57 red roses; still the same total of 417). I didn't want to mess with installing GLPK right now, so I used the PuLP default CBC solver.
Python version:
Code:
#################################
from ayx import Alteryx
Alteryx.installPackages(['pulp'])
from pandas import concat, DataFrame, Series
from pulp import GLPK, LpMaximize, LpProblem, LpStatus, lpSum, LpVariable
#################################
# read in the flower costs from the workflow and update the name
df = Alteryx.read('#1')
df.index = df['Flower'].str.replace(' ', '_').str.lower()
df.index.name = 'index'
#################################
# create a dictionary of cost for each flower name
costs = Series(data=df['Price']).to_dict()
costs
#################################
# define the model
model = LpProblem(name="flower-arrangements", sense=LpMaximize)
#################################
# define the decision variables
# this creates a dictionary of LpVariables with the same keys as costs
counts = {}
for k in costs.keys():
if k == 'filler_greens':
lb = 30
ub = 40
else:
lb = 40
ub = 80
counts[k] = LpVariable(name=k, lowBound=lb, upBound=ub, cat='Integer')
#################################
# add constraints
model += lpSum(costs[i] * counts[i] for i in costs.keys()) <= 950, \
'Total cost constraint'
#################################
# Set the objective (maximize the total number of flowers)
model += lpSum(counts[i] for i in counts.keys()), \
'Total number of flowers'
#################################
# Solve the optimization problem
status = model.solve()
# Get the results
print('--- status ---')
print(f"status: {model.status}, {LpStatus[model.status]}")
print('\n--- objective value ---')
print(f"objective: {model.objective.value()}")
print('\n--- variable values ---')
for v in counts.values():
print(f"{v.name}: {v.value()}")
print('\n--- constraints ---')
for name, constraint in model.constraints.items():
print(f"{name}: {constraint.value()}")
model.solver
#################################
# add the variables to a dataframe for output
df_vars = DataFrame({ 'value': [v.value() for v in counts.values()] },
index = [v.name for v in counts.values()])
df_vars.index.name = 'index'
#################################
# format the dataframe for output to the workflow
# with the original flower name
df_out = df[['Flower']].merge(df_vars, on='index')
df_out.columns = ['name', 'value']
#################################
# add a row for the objective value
df_out = concat([df_out, DataFrame({ 'name' : 'Objective Value',
'value' : model.objective.value() },
index=[0])] )
#################################
Alteryx.write(df_out, 1)
Alteryx version (variables in rows):
good practice for an optimization problem...
Optimization problems are not my friend... but, weirdly enough, approaching this using the Manual Input Mode this time ended up making way more sense than any of the other configuration methods, so this was at least a good exercise in better understanding the configurations for the Optimization tool!
Cheers!
NJ