{"id":3979,"date":"2025-05-21T07:02:20","date_gmt":"2025-05-21T07:02:20","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/05\/21\/optimizing-multi-objective-problems-with-desirability-functions\/"},"modified":"2025-05-21T07:02:20","modified_gmt":"2025-05-21T07:02:20","slug":"optimizing-multi-objective-problems-with-desirability-functions","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/05\/21\/optimizing-multi-objective-problems-with-desirability-functions\/","title":{"rendered":"Optimizing Multi-Objective Problems with Desirability Functions"},"content":{"rendered":"<p>    Optimizing Multi-Objective Problems with Desirability Functions<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<p class=\"wp-block-paragraph\"><mdspan datatext=\"el1747766571172\" class=\"mdspan-comment\">When working<\/mdspan> in <a href=\"https:\/\/towardsdatascience.com\/tag\/data-science\/\" title=\"Data Science\">Data Science<\/a>, it is not uncommon to encounter problems with competing objectives. Whether designing products, tuning algorithms or optimizing portfolios, we often need to balance several metrics to get the best possible outcome. Sometimes, maximizing one metrics comes at the expense of another, making it hard to have an overall optimized solution.<\/p>\n<p class=\"wp-block-paragraph\">While several solutions exist to solve multi-objective <a href=\"https:\/\/towardsdatascience.com\/tag\/optimization\/\" title=\"Optimization\">Optimization<\/a> problems, I found desirability function to be both elegant and easy to explain to non-technical audience. Which makes them an interesting option to consider. Desirability functions will combine several metrics into a standardized score, allowing for a holistic optimization.<\/p>\n<p class=\"wp-block-paragraph\">In this article, we\u2019ll explore:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">The mathematical foundation of desirability functions<\/li>\n<li class=\"wp-block-list-item\">How to implement these functions in <a href=\"https:\/\/towardsdatascience.com\/tag\/python\/\" title=\"Python\">Python<\/a>\n<\/li>\n<li class=\"wp-block-list-item\">How to optimize a multi-objective problem with desirability functions<\/li>\n<li class=\"wp-block-list-item\">Visualization for interpretation and explanation of the results<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">To ground these concepts in a real example, we\u2019ll apply desirability functions to optimize a bread baking: a toy problem with a few, interconnected parameters and competing quality objectives that will allow us to explore several optimization choices.<\/p>\n<p class=\"wp-block-paragraph\">By the end of this article, you\u2019ll have a powerful new tool in your data science toolkit for tackling multi-objective optimization problems across numerous domains, as well as a fully functional code available here on <a href=\"https:\/\/github.com\/vincent-vdb\/medium_posts\">GitHub<\/a>.<\/p>\n<h3 class=\"wp-block-heading\">What are Desirability Functions?<\/h3>\n<p class=\"wp-block-paragraph\">Desirability functions were first formalized by <a href=\"https:\/\/www.scirp.org\/reference\/referencespapers?referenceid=1542744\">Harrington (1965)<\/a> and later extended by <a href=\"https:\/\/www.tandfonline.com\/doi\/abs\/10.1080\/00224065.1980.11980968\">Derringer and Suich (1980)<\/a>. The idea is to:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Transform each response into a performance score between 0 (absolutely unacceptable) and 1 (the ideal value)<\/li>\n<li class=\"wp-block-list-item\">Combine all scores into a single metric to maximize<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Let\u2019s explore the types of desirability functions and then how we can combine all the scores.<\/p>\n<h4 class=\"wp-block-heading\">The different types of desirability functions<\/h4>\n<p class=\"wp-block-paragraph\">There are three different desirability functions, that would allow to handle many situations.<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Smaller-is-better<\/strong>: Used when minimizing a response is desirable<\/li>\n<\/ul>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def desirability_smaller_is_better(x: float, x_min: float, x_max: float) -&gt; float:\n    \"\"\"Calculate desirability function value where smaller values are better.\n\n    Args:\n        x: Input parameter value\n        x_min: Minimum acceptable value\n        x_max: Maximum acceptable value\n\n    Returns:\n        Desirability score between 0 and 1\n    \"\"\"\n    if x &lt;= x_min:\n        return 1.0\n    elif x &gt;= x_max:\n        return 0.0\n    else:\n        return (x_max - x) \/ (x_max - x_min)<\/code><\/pre>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Larger-is-better<\/strong>: Used when maximizing a response is desirable<\/li>\n<\/ul>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def desirability_larger_is_better(x: float, x_min: float, x_max: float) -&gt; float:\n    \"\"\"Calculate desirability function value where larger values are better.\n\n    Args:\n        x: Input parameter value\n        x_min: Minimum acceptable value\n        x_max: Maximum acceptable value\n\n    Returns:\n        Desirability score between 0 and 1\n    \"\"\"\n    if x &lt;= x_min:\n        return 0.0\n    elif x &gt;= x_max:\n        return 1.0\n    else:\n        return (x - x_min) \/ (x_max - x_min)<\/code><\/pre>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Target-is-best<\/strong>: Used when a specific target value is optimal<\/li>\n<\/ul>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def desirability_target_is_best(x: float, x_min: float, x_target: float, x_max: float) -&gt; float:\n    \"\"\"Calculate two-sided desirability function value with target value.\n\n    Args:\n        x: Input parameter value\n        x_min: Minimum acceptable value\n        x_target: Target (optimal) value\n        x_max: Maximum acceptable value\n\n    Returns:\n        Desirability score between 0 and 1\n    \"\"\"\n    if x_min &lt;= x &lt;= x_target:\n        return (x - x_min) \/ (x_target - x_min)\n    elif x_target &lt; x &lt;= x_max:\n        return (x_max - x) \/ (x_max - x_target)\n    else:\n        return 0.0<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Every input parameter can be parameterized with one of these three desirability functions, before combining them into a single desirability score.<\/p>\n<h4 class=\"wp-block-heading\">Combining Desirability Scores<\/h4>\n<p class=\"wp-block-paragraph\">Once individual metrics are transformed into desirability scores, they need to be combined into an overall desirability. The most common approach is the geometric mean:<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-143.png?ssl=1\" alt=\"\" class=\"wp-image-604344\"><\/figure>\n<p class=\"wp-block-paragraph\">Where d<sub>i<\/sub> are individual desirability values and w<sub>i<\/sub> are weights reflecting the relative importance of each metric.<\/p>\n<p class=\"wp-block-paragraph\">The geometric mean has an important property: if any single desirability is 0 (i.e. completely unacceptable), the overall desirability is also 0, regardless of other values. This enforces that all requirements must be met to some extent.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def overall_desirability(desirabilities, weights=None):\n    \"\"\"Compute overall desirability using geometric mean\n    \n    Parameters:\n    -----------\n    desirabilities : list\n        Individual desirability scores\n    weights : list\n        Weights for each desirability\n        \n    Returns:\n    --------\n    float\n        Overall desirability score\n    \"\"\"\n    if weights is None:\n        weights = [1] * len(desirabilities)\n        \n    # Convert to numpy arrays\n    d = np.array(desirabilities)\n    w = np.array(weights)\n    \n    # Calculate geometric mean\n    return np.prod(d ** w) ** (1 \/ np.sum(w))<\/code><\/pre>\n<p class=\"wp-block-paragraph\">The weights are hyperparameters that give leverage on the final outcome and give room for customization.<\/p>\n<h3 class=\"wp-block-heading\">A Practical Optimization Example: Bread\u00a0Baking<\/h3>\n<p class=\"wp-block-paragraph\">To demonstrate desirability functions in action, let\u2019s apply them to a toy problem: a bread baking optimization problem.<\/p>\n<h4 class=\"wp-block-heading\">The Parameters and Quality\u00a0Metrics<\/h4>\n<p class=\"wp-block-paragraph\">Let\u2019s play with the following parameters:<\/p>\n<ol class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Fermentation Time<\/strong> (30\u2013180 minutes)<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Fermentation Temperature<\/strong> (20\u201330\u00b0C)<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Hydration Level<\/strong> (60\u201385%)<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Kneading Time<\/strong> (0\u201320 minutes)<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Baking Temperature<\/strong> (180\u2013250\u00b0C)<\/li>\n<\/ol>\n<p class=\"wp-block-paragraph\">And let\u2019s try to optimize these metrics:<\/p>\n<ol class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Texture Quality<\/strong>: The texture of the bread<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Flavor Profile<\/strong>: The flavor of the bread<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Practicality<\/strong>: The practicality of the whole process<\/li>\n<\/ol>\n<p class=\"wp-block-paragraph\">Of course, each of these metrics depends on more than one parameter. So here comes one of the most critical steps: mapping parameters to quality metrics.\u00a0<\/p>\n<p class=\"wp-block-paragraph\">For each quality metric, we need to define how parameters influence it:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def compute_flavor_profile(params: List[float]) -&gt; float:\n    \"\"\"Compute flavor profile score based on input parameters.\n\n    Args:\n        params: List of parameter values [fermentation_time, ferment_temp, hydration,\n               kneading_time, baking_temp]\n\n    Returns:\n        Weighted flavor profile score between 0 and 1\n    \"\"\"\n    # Flavor mainly affected by fermentation parameters\n    fermentation_d = desirability_larger_is_better(params[0], 30, 180)\n    ferment_temp_d = desirability_target_is_best(params[1], 20, 24, 28)\n    hydration_d = desirability_target_is_best(params[2], 65, 75, 85)\n\n    # Baking temperature has minimal effect on flavor\n    weights = [0.5, 0.3, 0.2]\n    return np.average([fermentation_d, ferment_temp_d, hydration_d],\n                      weights=weights)<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Here for example, the flavor is influenced by the following:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">The fermentation time, with a minimum desirability below 30 minutes and a maximum desirability above 180 minutes<\/li>\n<li class=\"wp-block-list-item\">The fermentation temperature, with a maximum desirability peaking at 24 degrees Celsius<\/li>\n<li class=\"wp-block-list-item\">The hydration, with a maximum desirability peaking at 75% humidity<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">These computed parameters are then weighted averaged to return the flavor desirability. Similar computations and made for the texture quality and practicality.<\/p>\n<h4 class=\"wp-block-heading\">The Objective Function<\/h4>\n<p class=\"wp-block-paragraph\">Following the desirability function approach, we\u2019ll use the overall desirability as our objective function. The goal is to maximize this overall score, which means finding parameters that best satisfy all our three requirements simultaneously:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def objective_function(params: List[float], weights: List[float]) -&gt; float:\n    \"\"\"Compute overall desirability score based on individual quality metrics.\n\n    Args:\n        params: List of parameter values\n        weights: Weights for texture, flavor and practicality scores\n\n    Returns:\n        Negative overall desirability score (for minimization)\n    \"\"\"\n    # Compute individual desirability scores\n    texture = compute_texture_quality(params)\n    flavor = compute_flavor_profile(params)\n    practicality = compute_practicality(params)\n\n    # Ensure weights sum up to one\n    weights = np.array(weights) \/ np.sum(weights)\n\n    # Calculate overall desirability using geometric mean\n    overall_d = overall_desirability([texture, flavor, practicality], weights)\n\n    # Return negative value since we want to maximize desirability\n    # but optimization functions typically minimize\n    return -overall_d<\/code><\/pre>\n<p class=\"wp-block-paragraph\">After computing the individual desirabilities for texture, flavor and practicality; the overall desirability is simply computed with a weighted geometric mean. It finally returns the negative overall desirability, so that it can be minimized.<\/p>\n<h4 class=\"wp-block-heading\">Optimization with\u00a0SciPy<\/h4>\n<p class=\"wp-block-paragraph\">We finally use <a href=\"https:\/\/docs.scipy.org\/doc\/scipy\/reference\/generated\/scipy.optimize.minimize.html\">SciPy<\/a>\u2019s <code>minimize<\/code> function to find optimal parameters. Since we returned the negative overall desirability as the objective function, minimizing it would maximize the overall desirability:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def optimize(weights: list[float]) -&gt; list[float]:\n    # Define parameter bounds\n    bounds = {\n        'fermentation_time': (1, 24),\n        'fermentation_temp': (20, 30),\n        'hydration_level': (60, 85),\n        'kneading_time': (0, 20),\n        'baking_temp': (180, 250)\n    }\n\n    # Initial guess (middle of bounds)\n    x0 = [(b[0] + b[1]) \/ 2 for b in bounds.values()]\n\n    # Run optimization\n    result = minimize(\n        objective_function,\n        x0,\n        args=(weights,),\n        bounds=list(bounds.values()),\n        method='SLSQP'\n    )\n\n    return result.x<\/code><\/pre>\n<p class=\"wp-block-paragraph\">In this function, after defining the bounds for each parameter, the initial guess is computed as the middle of bounds, and then given as input to the <code>minimize<\/code> function of SciPy. The result is finally returned.\u00a0<\/p>\n<p class=\"wp-block-paragraph\">The weights are given as input to the optimizer too, and are a good way to customize the output. For example, with a larger weight on practicality, the optimized solution will focus on practicality over flavor and texture.<\/p>\n<p class=\"wp-block-paragraph\">Let\u2019s now visualize the results for a few sets of weights.<\/p>\n<h4 class=\"wp-block-heading\">Visualization of Results<\/h4>\n<p class=\"wp-block-paragraph\">Let\u2019s see how the optimizer handles different preference profiles, demonstrating the flexibility of desirability functions, given various input weights.<\/p>\n<p class=\"wp-block-paragraph\">Let\u2019s have a look at the results in case of weights favoring practicality:<\/p>\n<figure class=\"wp-block-image alignwide size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/1On8XKxiMTGedqV-SuNanhQ.png?ssl=1\" alt=\"\" class=\"wp-image-604346\"><figcaption class=\"wp-element-caption\">Optimized parameters with weights favoring practicality. Image by\u00a0author.<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">With weights largely in favor of practicality, the achieved overall desirability is 0.69, with a short kneading time of 5 minutes, since a high value impacts negatively the practicality.<\/p>\n<p class=\"wp-block-paragraph\">Now, if we optimize with an emphasis on texture, we have slightly different results:<\/p>\n<figure class=\"wp-block-image alignwide size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/1b6jc6-Azr0g1XVTeD99e7w.png?ssl=1\" alt=\"\" class=\"wp-image-604345\"><figcaption class=\"wp-element-caption\">Optimized parameters with weights favoring texture. Image by\u00a0author.<\/figcaption><\/figure>\n<p class=\"wp-block-paragraph\">In this case, the achieved overall desirability is 0.85, significantly higher. The kneading time is this time 12 minutes, as a higher value impacts positively the texture and is not penalized so much because of practicality.\u00a0<\/p>\n<h3 class=\"wp-block-heading\">Conclusion: Practical Applications of Desirability Functions<\/h3>\n<p class=\"wp-block-paragraph\">While we focused on bread baking as our example, the same approach can be applied to various domains, such as product formulation in cosmetics or resource allocation in portfolio optimization.<\/p>\n<p class=\"wp-block-paragraph\">Desirability functions provide a powerful mathematical framework for tackling multi-objective optimization problems across numerous data science applications. By transforming raw metrics into standardized desirability scores, we can effectively combine and optimize disparate objectives.<\/p>\n<p class=\"wp-block-paragraph\">The key advantages of this approach include:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Standardized scales that make different metrics comparable and easy to combine into a single target<\/li>\n<li class=\"wp-block-list-item\">Flexibility to handle different types of objectives: minimize, maximize, target<\/li>\n<li class=\"wp-block-list-item\">Clear communication of preferences through mathematical functions<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">The code presented here provides a starting point for your own experimentation. Whether you\u2019re optimizing industrial processes, machine learning models, or product formulations, hopefully desirability functions offer a systematic approach to finding the best compromise among competing objectives.<\/p>\n<p>The post <a href=\"https:\/\/towardsdatascience.com\/optimizing-multi-objective-problems-with-desirability-functions\/\">Optimizing Multi-Objective Problems with Desirability Functions<\/a> appeared first on <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a>.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Vincent Vandenbussche<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/towardsdatascience.com\/optimizing-multi-objective-problems-with-desirability-functions\/\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Optimizing Multi-Objective Problems with Desirability Functions When working in Data Science, it is not uncommon to encounter problems with competing objectives. Whether designing products, tuning algorithms or optimizing portfolios, we often need to balance several metrics to get the best possible outcome. Sometimes, maximizing one metrics comes at the expense of another, making it hard [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,83,82,2726,402,157],"tags":[2727,1412,906],"class_list":["post-3979","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-data-science","category-data-visualization","category-multi-objective","category-optimization","category-python","tag-desirability","tag-functions","tag-multi"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/3979"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=3979"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/3979\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=3979"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=3979"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=3979"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}