{ "cells": [ { "cell_type": "markdown", "id": "cell-0", "metadata": {}, "source": [ "# Example of a D-efficient RUM design using the RSC algorithm\n", "\n", "This notebook illustrates how to use **ChoiceDesign** with the **RSC (Relabelling, Swapping, Cycling)** optimisation algorithm. RSC extends the basic random swapping approach with two additional move types:\n", "\n", "- **Relabelling (R):** swaps all occurrences of two level values in a column.\n", "- **Swapping (S):** swaps the values of two individual rows in a column (same as the default algorithm).\n", "- **Cycling (C):** rotates all values in a column by one position.\n", "\n", "Each iteration, a column and a move type are chosen at random. The move is kept if it improves the D-error.\n", "\n", "The design setup — attributes, utility functions, and stopping criteria — is identical to the `d_efficient_rum_simple` example. The only difference is the `algorithm='rsc'` argument passed to `optimise()`.\n", "\n", "## Step 1: Load modules, define design parameters and set attributes" ] }, { "cell_type": "code", "execution_count": 1, "id": "cell-1", "metadata": {}, "outputs": [], "source": [ "from choicedesign.design import EffDesign\n", "from choicedesign.expressions import Attribute, Parameter" ] }, { "cell_type": "markdown", "id": "cell-2", "metadata": {}, "source": [ "The following lines define 2 alternatives with 4 attributes each, named $A$ to $D$:" ] }, { "cell_type": "code", "execution_count": 2, "id": "cell-3", "metadata": {}, "outputs": [], "source": [ "alt1_A = Attribute('alt1_A', [1, 2, 3])\n", "alt1_B = Attribute('alt1_B', [10, 15, 15.5])\n", "alt1_C = Attribute('alt1_C', [0, 3, 5])\n", "alt1_D = Attribute('alt1_D', [0, 1, 2])\n", "\n", "alt2_A = Attribute('alt2_A', [1, 2, 3])\n", "alt2_B = Attribute('alt2_B', [10, 15, 15.5])\n", "alt2_C = Attribute('alt2_C', [0, 3, 5])\n", "alt2_D = Attribute('alt2_D', [0, 1, 2])" ] }, { "cell_type": "markdown", "id": "cell-4", "metadata": {}, "source": [ "## Step 2: Construct the design object and generate the initial design matrix" ] }, { "cell_type": "code", "execution_count": 3, "id": "cell-5", "metadata": {}, "outputs": [], "source": [ "design = EffDesign(\n", " X=[alt1_A, alt1_B, alt1_C, alt1_D,\n", " alt2_A, alt2_B, alt2_C, alt2_D],\n", " ncs=18)" ] }, { "cell_type": "code", "execution_count": 4, "id": "cell-6", "metadata": {}, "outputs": [ { "data": { "application/vnd.microsoft.datawrangler.viewer.v0+json": { "columns": [ { "name": "index", "rawType": "int64", "type": "integer" }, { "name": "alt1_A", "rawType": "float64", "type": "float" }, { "name": "alt1_B", "rawType": "float64", "type": "float" }, { "name": "alt1_C", "rawType": "float64", "type": "float" }, { "name": "alt1_D", "rawType": "float64", "type": "float" }, { "name": "alt2_A", "rawType": "float64", "type": "float" }, { "name": "alt2_B", "rawType": "float64", "type": "float" }, { "name": "alt2_C", "rawType": "float64", "type": "float" }, { "name": "alt2_D", "rawType": "float64", "type": "float" } ], "ref": "fb2612e7-d6bb-45a0-b0df-98299b40ed06", "rows": [ [ "0", "1.0", "10.0", "5.0", "1.0", "2.0", "15.5", "5.0", "2.0" ], [ "1", "2.0", "15.0", "3.0", "2.0", "3.0", "15.0", "5.0", "0.0" ], [ "2", "3.0", "10.0", "5.0", "1.0", "2.0", "15.5", "5.0", "1.0" ], [ "3", "3.0", "15.5", "0.0", "1.0", "1.0", "10.0", "3.0", "2.0" ], [ "4", "1.0", "15.5", "3.0", "0.0", "1.0", "15.5", "5.0", "0.0" ], [ "5", "2.0", "15.0", "3.0", "2.0", "3.0", "15.0", "3.0", "0.0" ], [ "6", "2.0", "10.0", "3.0", "2.0", "2.0", "10.0", "0.0", "1.0" ], [ "7", "1.0", "10.0", "0.0", "0.0", "3.0", "15.5", "0.0", "0.0" ], [ "8", "3.0", "15.5", "5.0", "0.0", "1.0", "10.0", "5.0", "2.0" ], [ "9", "3.0", "10.0", "0.0", "2.0", "2.0", "15.0", "0.0", "0.0" ], [ "10", "1.0", "10.0", "0.0", "0.0", "1.0", "15.5", "0.0", "0.0" ], [ "11", "3.0", "15.0", "5.0", "1.0", "3.0", "10.0", "3.0", "1.0" ], [ "12", "2.0", "15.0", "5.0", "2.0", "1.0", "15.0", "3.0", "1.0" ], [ "13", "1.0", "15.5", "3.0", "2.0", "3.0", "15.0", "0.0", "2.0" ], [ "14", "2.0", "15.5", "3.0", "0.0", "3.0", "15.5", "0.0", "1.0" ], [ "15", "2.0", "15.0", "5.0", "1.0", "2.0", "15.0", "5.0", "2.0" ], [ "16", "3.0", "15.5", "0.0", "1.0", "2.0", "10.0", "3.0", "2.0" ], [ "17", "1.0", "15.0", "0.0", "0.0", "1.0", "10.0", "3.0", "1.0" ] ], "shape": { "columns": 8, "rows": 18 } }, "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
alt1_Aalt1_Balt1_Calt1_Dalt2_Aalt2_Balt2_Calt2_D
01.010.05.01.02.015.55.02.0
12.015.03.02.03.015.05.00.0
23.010.05.01.02.015.55.01.0
33.015.50.01.01.010.03.02.0
41.015.53.00.01.015.55.00.0
52.015.03.02.03.015.03.00.0
62.010.03.02.02.010.00.01.0
71.010.00.00.03.015.50.00.0
83.015.55.00.01.010.05.02.0
93.010.00.02.02.015.00.00.0
101.010.00.00.01.015.50.00.0
113.015.05.01.03.010.03.01.0
122.015.05.02.01.015.03.01.0
131.015.53.02.03.015.00.02.0
142.015.53.00.03.015.50.01.0
152.015.05.01.02.015.05.02.0
163.015.50.01.02.010.03.02.0
171.015.00.00.01.010.03.01.0
\n", "
" ], "text/plain": [ " alt1_A alt1_B alt1_C alt1_D alt2_A alt2_B alt2_C alt2_D\n", "0 1.0 10.0 5.0 1.0 2.0 15.5 5.0 2.0\n", "1 2.0 15.0 3.0 2.0 3.0 15.0 5.0 0.0\n", "2 3.0 10.0 5.0 1.0 2.0 15.5 5.0 1.0\n", "3 3.0 15.5 0.0 1.0 1.0 10.0 3.0 2.0\n", "4 1.0 15.5 3.0 0.0 1.0 15.5 5.0 0.0\n", "5 2.0 15.0 3.0 2.0 3.0 15.0 3.0 0.0\n", "6 2.0 10.0 3.0 2.0 2.0 10.0 0.0 1.0\n", "7 1.0 10.0 0.0 0.0 3.0 15.5 0.0 0.0\n", "8 3.0 15.5 5.0 0.0 1.0 10.0 5.0 2.0\n", "9 3.0 10.0 0.0 2.0 2.0 15.0 0.0 0.0\n", "10 1.0 10.0 0.0 0.0 1.0 15.5 0.0 0.0\n", "11 3.0 15.0 5.0 1.0 3.0 10.0 3.0 1.0\n", "12 2.0 15.0 5.0 2.0 1.0 15.0 3.0 1.0\n", "13 1.0 15.5 3.0 2.0 3.0 15.0 0.0 2.0\n", "14 2.0 15.5 3.0 0.0 3.0 15.5 0.0 1.0\n", "15 2.0 15.0 5.0 1.0 2.0 15.0 5.0 2.0\n", "16 3.0 15.5 0.0 1.0 2.0 10.0 3.0 2.0\n", "17 1.0 15.0 0.0 0.0 1.0 10.0 3.0 1.0" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "init_design = design.gen_initdesign(seed=42)\n", "init_design" ] }, { "cell_type": "markdown", "id": "cell-7", "metadata": {}, "source": [ "## Step 3: Define the utility functions" ] }, { "cell_type": "code", "execution_count": 5, "id": "cell-8", "metadata": {}, "outputs": [], "source": [ "beta_A = Parameter('beta_A', -0.1)\n", "beta_B = Parameter('beta_B', -0.02)\n", "beta_C = Parameter('beta_C', 0.1)\n", "beta_D = Parameter('beta_D', 0.15)" ] }, { "cell_type": "code", "execution_count": 6, "id": "cell-9", "metadata": {}, "outputs": [], "source": [ "V1 = beta_A * alt1_A + beta_B * alt1_B + beta_C * alt1_C + beta_D * alt1_D\n", "V2 = beta_A * alt2_A + beta_B * alt2_B + beta_C * alt2_C + beta_D * alt2_D\n", "\n", "V = {1: V1, 2: V2}" ] }, { "cell_type": "markdown", "id": "cell-10", "metadata": {}, "source": [ "## Step 4: Optimise using the RSC algorithm\n", "\n", "The `algorithm='rsc'` argument selects the RSC algorithm. All other arguments are identical to the default `'swap'` algorithm. The optimisation runs for 1 minute:" ] }, { "cell_type": "code", "execution_count": 7, "id": "cell-11", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Evaluating initial design\n", "Optimization complete 0:00:59 / D-error: 0.033939\n", "Elapsed time: 0:01:00\n", "D-error of initial design: 0.080551\n", "D-error of last stored design: 0.033939\n", "Utility Balance ratio: 94.88 %\n", "Algorithm iterations: 148538\n", "\n" ] } ], "source": [ "optimal_design, init_perf, final_perf, final_iter, ubalance_ratio = design.optimise(\n", " init_design=init_design,\n", " V=V,\n", " model='mnl',\n", " algorithm='rsc',\n", " time_lim=1,\n", " verbose=True\n", ")" ] }, { "cell_type": "markdown", "id": "cell-12", "metadata": {}, "source": [ "## Step 5: Block the design" ] }, { "cell_type": "code", "execution_count": 8, "id": "cell-13", "metadata": {}, "outputs": [ { "data": { "application/vnd.microsoft.datawrangler.viewer.v0+json": { "columns": [ { "name": "index", "rawType": "int64", "type": "integer" }, { "name": "CS", "rawType": "float64", "type": "float" }, { "name": "alt1_A", "rawType": "float64", "type": "float" }, { "name": "alt1_B", "rawType": "float64", "type": "float" }, { "name": "alt1_C", "rawType": "float64", "type": "float" }, { "name": "alt1_D", "rawType": "float64", "type": "float" }, { "name": "alt2_A", "rawType": "float64", "type": "float" }, { "name": "alt2_B", "rawType": "float64", "type": "float" }, { "name": "alt2_C", "rawType": "float64", "type": "float" }, { "name": "alt2_D", "rawType": "float64", "type": "float" }, { "name": "Block", "rawType": "int64", "type": "integer" } ], "ref": "a3f2f157-fca8-473d-b478-b5d556f94e02", "rows": [ [ "0", "1.0", "1.0", "10.0", "5.0", "1.0", "3.0", "15.5", "0.0", "1.0", "2" ], [ "1", "2.0", "2.0", "15.5", "3.0", "2.0", "2.0", "10.0", "3.0", "0.0", "3" ], [ "2", "3.0", "3.0", "15.0", "5.0", "0.0", "1.0", "15.0", "0.0", "2.0", "3" ], [ "3", "4.0", "1.0", "15.5", "0.0", "1.0", "3.0", "10.0", "5.0", "1.0", "1" ], [ "4", "5.0", "3.0", "15.0", "3.0", "0.0", "1.0", "15.0", "3.0", "2.0", "1" ], [ "5", "6.0", "2.0", "10.0", "0.0", "2.0", "2.0", "15.5", "5.0", "0.0", "3" ], [ "6", "7.0", "2.0", "15.0", "5.0", "2.0", "2.0", "15.0", "0.0", "0.0", "1" ], [ "7", "8.0", "1.0", "10.0", "3.0", "2.0", "3.0", "15.5", "3.0", "0.0", "2" ], [ "8", "9.0", "3.0", "10.0", "3.0", "0.0", "1.0", "15.5", "3.0", "2.0", "2" ], [ "9", "10.0", "3.0", "10.0", "0.0", "2.0", "1.0", "15.5", "5.0", "0.0", "1" ], [ "10", "11.0", "1.0", "15.0", "3.0", "0.0", "3.0", "15.0", "3.0", "2.0", "3" ], [ "11", "12.0", "3.0", "15.5", "5.0", "1.0", "1.0", "10.0", "0.0", "1.0", "1" ], [ "12", "13.0", "3.0", "15.5", "3.0", "2.0", "1.0", "10.0", "3.0", "0.0", "3" ], [ "13", "14.0", "1.0", "15.0", "5.0", "1.0", "3.0", "15.0", "0.0", "1.0", "2" ], [ "14", "15.0", "2.0", "10.0", "0.0", "0.0", "2.0", "15.5", "5.0", "2.0", "2" ], [ "15", "16.0", "2.0", "15.0", "5.0", "0.0", "2.0", "15.0", "0.0", "2.0", "2" ], [ "16", "17.0", "2.0", "15.5", "0.0", "1.0", "2.0", "10.0", "5.0", "1.0", "3" ], [ "17", "18.0", "1.0", "15.5", "0.0", "1.0", "3.0", "10.0", "5.0", "1.0", "1" ] ], "shape": { "columns": 10, "rows": 18 } }, "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
CSalt1_Aalt1_Balt1_Calt1_Dalt2_Aalt2_Balt2_Calt2_DBlock
01.01.010.05.01.03.015.50.01.02
12.02.015.53.02.02.010.03.00.03
23.03.015.05.00.01.015.00.02.03
34.01.015.50.01.03.010.05.01.01
45.03.015.03.00.01.015.03.02.01
56.02.010.00.02.02.015.55.00.03
67.02.015.05.02.02.015.00.00.01
78.01.010.03.02.03.015.53.00.02
89.03.010.03.00.01.015.53.02.02
910.03.010.00.02.01.015.55.00.01
1011.01.015.03.00.03.015.03.02.03
1112.03.015.55.01.01.010.00.01.01
1213.03.015.53.02.01.010.03.00.03
1314.01.015.05.01.03.015.00.01.02
1415.02.010.00.00.02.015.55.02.02
1516.02.015.05.00.02.015.00.02.02
1617.02.015.50.01.02.010.05.01.03
1718.01.015.50.01.03.010.05.01.01
\n", "
" ], "text/plain": [ " CS alt1_A alt1_B alt1_C alt1_D alt2_A alt2_B alt2_C alt2_D \\\n", "0 1.0 1.0 10.0 5.0 1.0 3.0 15.5 0.0 1.0 \n", "1 2.0 2.0 15.5 3.0 2.0 2.0 10.0 3.0 0.0 \n", "2 3.0 3.0 15.0 5.0 0.0 1.0 15.0 0.0 2.0 \n", "3 4.0 1.0 15.5 0.0 1.0 3.0 10.0 5.0 1.0 \n", "4 5.0 3.0 15.0 3.0 0.0 1.0 15.0 3.0 2.0 \n", "5 6.0 2.0 10.0 0.0 2.0 2.0 15.5 5.0 0.0 \n", "6 7.0 2.0 15.0 5.0 2.0 2.0 15.0 0.0 0.0 \n", "7 8.0 1.0 10.0 3.0 2.0 3.0 15.5 3.0 0.0 \n", "8 9.0 3.0 10.0 3.0 0.0 1.0 15.5 3.0 2.0 \n", "9 10.0 3.0 10.0 0.0 2.0 1.0 15.5 5.0 0.0 \n", "10 11.0 1.0 15.0 3.0 0.0 3.0 15.0 3.0 2.0 \n", "11 12.0 3.0 15.5 5.0 1.0 1.0 10.0 0.0 1.0 \n", "12 13.0 3.0 15.5 3.0 2.0 1.0 10.0 3.0 0.0 \n", "13 14.0 1.0 15.0 5.0 1.0 3.0 15.0 0.0 1.0 \n", "14 15.0 2.0 10.0 0.0 0.0 2.0 15.5 5.0 2.0 \n", "15 16.0 2.0 15.0 5.0 0.0 2.0 15.0 0.0 2.0 \n", "16 17.0 2.0 15.5 0.0 1.0 2.0 10.0 5.0 1.0 \n", "17 18.0 1.0 15.5 0.0 1.0 3.0 10.0 5.0 1.0 \n", "\n", " Block \n", "0 2 \n", "1 3 \n", "2 3 \n", "3 1 \n", "4 1 \n", "5 3 \n", "6 1 \n", "7 2 \n", "8 2 \n", "9 1 \n", "10 3 \n", "11 1 \n", "12 3 \n", "13 2 \n", "14 2 \n", "15 2 \n", "16 3 \n", "17 1 " ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "optimal_design_blocked, corr_history = design.gen_blocks(optimal_design, n_blocks=3)\n", "optimal_design_blocked" ] }, { "cell_type": "markdown", "id": "cell-14", "metadata": {}, "source": [ "## (Optional) Evaluate the design" ] }, { "cell_type": "code", "execution_count": 9, "id": "cell-15", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.033939284712415994 94.87569390189945\n" ] } ], "source": [ "perf, ubalance = design.evaluate(optimal_design, V, model='mnl')\n", "print(perf, ubalance)" ] }, { "cell_type": "markdown", "id": "dfec30b0", "metadata": {}, "source": [ "## Export the design\n", "\n", "Export the RSC-optimised design to Excel." ] }, { "cell_type": "code", "execution_count": null, "id": "70e262ec", "metadata": {}, "outputs": [], "source": [ "attr_names = {\n", " 'alt1_A': 'Attribute A', 'alt2_A': 'Attribute A',\n", " 'alt1_B': 'Attribute B', 'alt2_B': 'Attribute B',\n", " 'alt1_C': 'Attribute C', 'alt2_C': 'Attribute C',\n", " 'alt1_D': 'Attribute D', 'alt2_D': 'Attribute D',\n", "}\n", "design.export_design(optimal_design, attr_names, 'rum_rsc_design.xlsx')" ] }, { "cell_type": "markdown", "id": "8cbc2212", "metadata": {}, "source": [ "## Save the optimisation summary\n", "\n", "After calling `optimise()`, the method `export_output()` writes a plain-text summary of the optimisation run — design configuration, stopping criteria, criterion values, utility balance, elapsed time, and iteration count — to a file." ] }, { "cell_type": "code", "execution_count": null, "id": "12f0e9f7", "metadata": {}, "outputs": [], "source": [ "design.export_output('rum_rsc_output.txt')" ] }, { "cell_type": "markdown", "id": "cell-16", "metadata": {}, "source": [ "## References\n", "\n", "[1] Quan, W., Rose, J. M., Collins, A. T., & Bliemer, M. C. (2011). A comparison of algorithms for generating efficient choice experiments." ] } ], "metadata": { "kernelspec": { "display_name": "choicedesign-oSBhddzi-py3.13", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.1" } }, "nbformat": 4, "nbformat_minor": 5 }