{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Dominance and satisfaction analysis (AKA filters)\n", "\n", "This tutorial provides a practical overview of how to use scikit-criteria for \n", "satisfaction and dominance analysis, as well as the creation of filters for \n", "data cleaning.\n", "\n", "\n", "## Case\n", "\n", "In order to decide to purchase a series of bonds, a company studied five \n", "candidate investments: *PE*, *JN*, *AA*, *FX*, *MM* and *GN*. \n", "\n", "The finance department decides to consider the following criteria for selection. \n", "selection:\n", "\n", "1. **ROE:** Return percentage. Sense of optimality, \n", " $Maximize$.\n", "2. **CAP:** Market capitalization. Sense of optimality, $Maximize$. \n", "3. **RI:** Risk. Sense of optimality, $Minimize$. \n", "\n", "The full decision matrix" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "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", "
ROE[▲ 1.0]CAP[▲ 1.0]RI[▼ 1.0]
PE7535
JN5426
AA5628
FX3436
MM1730
FN5830
\n", "
6 Alternatives x 3 Criteria\n", "
" ], "text/plain": [ " ROE[▲ 1.0] CAP[▲ 1.0] RI[▼ 1.0]\n", "PE 7 5 35\n", "JN 5 4 26\n", "AA 5 6 28\n", "FX 3 4 36\n", "MM 1 7 30\n", "FN 5 8 30\n", "[6 Alternatives x 3 Criteria]" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import skcriteria as skc\n", "\n", "dm = skc.mkdm(\n", " matrix=[\n", " [7, 5, 35],\n", " [5, 4, 26],\n", " [5, 6, 28],\n", " [3, 4, 36],\n", " [1, 7, 30],\n", " [5, 8, 30],\n", " ],\n", " objectives=[max, max, min],\n", " alternatives=[\"PE\", \"JN\", \"AA\", \"FX\", \"MM\", \"FN\"],\n", " criteria=[\"ROE\", \"CAP\", \"RI\"],\n", ")\n", "\n", "dm" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Satisfaction analysis\n", "\n", "It is reasonable to think that any decision-maker would want to set \n", "\"satisfaction thresholds\" for each criterion, in such a way that alternatives \n", "that do not exceed the thresholds in any criterion are eliminated.\n", "\n", "The basic idea was proposed in the work of \n", "\"_A Behavioral Model of Rational Choice_\" \n", "[simon1955behavioral] and presents \n", "the definition of \"*aspiration levels*\" and are set a priori by the decision maker.\n", "\n", "\n", "> For our example we will assume that the decision-maker only accepts alternatives \n", "> with $ROE >= 2%$.\n", "\n", "For this analysis we will need the `skcriteria.preprocessing.filters` module ." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from skcriteria.preprocessing import filters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The filters are *transformers* and works as follows:\n", "\n", "- At the moment of construction they are provided with a dict that as a key has \n", " the name of a criterion, and as a value the condition to be satisfied.\n", "- Optionally it receives a parameter `ignore_missing_criteria` which if it is \n", " set to False (default value) fails any attempt to transform an decision \n", " matrix that does not have any of the criteria.\n", "- For an alternative not to be eliminated the alternative has to pass all \n", " filter conditions. \n", "\n", "The simplest filter consists of instances of the class ``filters.Filters``,\n", "which as a value of the configuration dict, accepts functions that are applied \n", "to the corresponding criteria and returns a mask where the `True` values denote \n", "the alternatives that we want to keep.\n", "\n", "To write the function that filters the alternatives where $ROE >= 2." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "}, ignore_missing_criteria=False]>" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def roe_filter(v):\n", " return v >= 2 # criteria are numpy.ndarray\n", "\n", "flt = filters.Filter({\"ROE\": roe_filter})\n", "flt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, `scikit-criteria` offers a simpler collection of filters\n", "that implements the most common operations of equality, inequality and \n", "inclusion a set.\n", "\n", "In our case we are interested in the `FilterGE` class, where GE stands for\n", "*Greater or Equal*.\n", "\n", "So the filter would be defined as" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "flt = filters.FilterGE({\"ROE\": 2})\n", "flt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The way to apply the filter to a `DecisionMatrix`, is like any other \n", "transformer: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "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", "
ROE[▲ 1.0]CAP[▲ 1.0]RI[▼ 1.0]
PE7535
JN5426
AA5628
FX3436
FN5830
\n", "
5 Alternatives x 3 Criteria\n", "
" ], "text/plain": [ " ROE[▲ 1.0] CAP[▲ 1.0] RI[▼ 1.0]\n", "PE 7 5 35\n", "JN 5 4 26\n", "AA 5 6 28\n", "FX 3 4 36\n", "FN 5 8 30\n", "[5 Alternatives x 3 Criteria]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dmf = flt.transform(dm)\n", "dmf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As can be seen, we eliminated the alternative `MM` which did not comply with an\n", "$ROE >= 2$.\n", "\n", "If on the other hand (to give an example) we would like to filter out the alternatives\n", "$ROE > 3$ and $CAP > 4$ (using the original matrix), we can use the \n", "filter `FilterGT` where GT is *Greater Than*." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "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", "
ROE[▲ 1.0]CAP[▲ 1.0]RI[▼ 1.0]
PE7535
AA5628
FN5830
\n", "
3 Alternatives x 3 Criteria\n", "
" ], "text/plain": [ " ROE[▲ 1.0] CAP[▲ 1.0] RI[▼ 1.0]\n", "PE 7 5 35\n", "AA 5 6 28\n", "FN 5 8 30\n", "[3 Alternatives x 3 Criteria]" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "filters.FilterGT({\"ROE\": 3, \"CAP\": 4}).transform(dm)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "**Note:** \n", "\n", "If it is necessary to filter the alternatives by two separate conditions, \n", "a pipeline can be used. An example of this can be seen below, where we combine \n", "a satisficing and a dominance filter\n", "
\n", "\n", "The complete list of filters implemented by Scikit-Criteria is:\n", "\n", "- ``filters.Filter``: Filter alternatives according to the value of a criterion \n", " using arbitrary functions.\n", "\n", " ```python\n", " filters.Filter({\"criterion\": lambda v: v > 1})\n", " ```\n", "\n", "- ``filters.FilterGT``: Filter Greater Than ($>$).\n", " \n", " ```python\n", " filters.FilterGT({\"criterion\": 1})\n", " ```\n", "\n", "- ``filters.FilterGE``: Filter Greater or Equal than ($>=$).\n", "\n", " ```python\n", " filters.FilterGE({\"criterion\": 2})\n", " ```\n", "\n", "- ``filters.FilterLT``: Filter Less Than ($<$).\n", " \n", " ```python\n", " filters.FilterLT({\"criterion\": 1})\n", " ```\n", "\n", "- ``filters.FilterLE``: Filter Less or Equal than ($<=$).\n", "\n", " ```python\n", " filters.FilterLE({\"criterion\": 2})\n", " ```\n", "\n", "- ``filters.FilterEQ``: Filter Equal ($==$).\n", " \n", " ```python\n", " filters.FilterEQ({\"criterion\": 1})\n", " ```\n", "\n", "- ``filters.FilterNE``: Filter Not-Equal than ($!=$).\n", "\n", " ```python\n", " filters.FilterNE({\"criterion\": 2})\n", " ```\n", "\n", "- ``filters.FilterIn``: Filter if the values is in a set ($\\in$).\n", " \n", " ```python\n", " filters.FilterIn({\"criterion\": [1, 2, 3]})\n", " ```\n", "\n", "- ``filters.FilterNotIn``: Filter if the values is not in a set ($\\notin$).\n", "\n", " ```python\n", " filters.FilterNotIn({\"criterion\": [1, 2, 3]})\n", " ```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dominance\n", "\n", "An alternative $A_0$ is said to dominate an alternative $A_1$ \n", "($A_0 \\succeq A_1$), if $A_0$ is equal in all criteria and better in at least \n", "one criterion. On the other hand, $A_0$ strictly dominate $A_1$ \n", "($A_0$ \\succeq A_1$). $A_1$ ($A_0 \\succ A_1$), if $A_0$ is better on all \n", "criteria than $A_1$.\n", "\n", "\n", "Under this same train of thought, an alternative that dominates all others is \n", "called a \"_dominant alternative_\". If there is a dominant alternative, it is \n", "undoubtedly the best choice, as long as a full ranking is not required.\n", "\n", "On the other hand, an *alternative is dominated* if there exists at least one \n", "other alternative that dominates it. If a dominated alternative exists and a \n", "consigned ordering is not desired, it must be removed from the set of decision \n", "alternatives. \n", "\n", "Generally only the non-dominated or efficient alternatives are the interested\n", " ones.\n", "\n", "### Scikit-Criteria dominance analysis\n", "\n", "Scikit-criteria, contains a number of tools within the attribute,\n", "`DecisionMatrix.dominance`, useful for the evaluation of dominant and dominated \n", "alternatives.\n", "\n", "For example, we can access all the dominated alternatives by using\n", "the `dominated` method" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Alternatives\n", "PE False\n", "JN False\n", "AA False\n", "FX True\n", "FN False\n", "Name: Dominated, dtype: bool" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dmf.dominance.dominated()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It can be seen with this, that `FX` is an dominated alternative. In addition \n", "if we want to know which are the *strictly dominated* alternatives we need to \n", "provide the `strict` parameter to the method:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Alternatives\n", "PE False\n", "JN False\n", "AA False\n", "FX True\n", "FN False\n", "Name: Strictly dominated, dtype: bool" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dmf.dominance.dominated(strict=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It can be seen that *FX* is strictly dominated by at least one other alternative.\n", "\n", "If we wanted to find out which are the dominant alternatives of *FX*, \n", "we can opt for two paths: \n", "\n", "1. List all the dominant/strictly dominated alternatives of \n", " *FX* using `dominator_of()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array(['PE', 'AA', 'FN'], dtype=object)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dmf.dominance.dominators_of(\"FX\", strict=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. Use `dominance()`/`dominance.dominance()` to see the full relationship \n", " between all alternatives." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "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", "
Strictly dominatedPEJNAAFXFN
Strict dominators
PEFalseFalseFalseTrueFalse
JNFalseFalseFalseFalseFalse
AAFalseFalseFalseTrueFalse
FXFalseFalseFalseFalseFalse
FNFalseFalseFalseTrueFalse
\n", "
" ], "text/plain": [ "Strictly dominated PE JN AA FX FN\n", "Strict dominators \n", "PE False False False True False\n", "JN False False False False False\n", "AA False False False True False\n", "FX False False False False False\n", "FN False False False True False" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dmf.dominance(strict=True) # equivalent to dmf.dominance.dominance()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "the result of the method is a \n", "[DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html) \n", "that in each cell has a `True` value if the _row alternative_ dominates the \n", "_column alternative_.\n", "\n", "If this matrix is very large: we can, for example, visualize it " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "image/png": "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uKE7AhEeHoH5C5aLPnZJxVzoFuZDdzAXuoO5UE52ZuGn0d7idxupSRNvQgAID49AyOZdGhm7pGIudAvzoTuYC93BXIhLqzMv+vr6ePz4MSwsLAC8mXmJiYmBvb39B3oWrjhmXoiIiEhcys68aPWaF0EQ4OnpKb9bKD09HcOHD4epqalCux07dmgjPCIiItJBWi1eBg4cqPB6wADVv/OBiIiIPi5aPW2kKTxtREREJD28YJeIiIhKJBYvREREJCksXoiIiEhSWLwQERGRpLB4ISIiIklh8UJERESSwuKFiIiIJIXFCxEREUkKixciIiKSFBYvREREJCksXoiIiEhSWLwQERGRpLB4ISIiIklh8UJERESSwuKFiIiIJIXFCxEREUkKixciIiKSFBYvREREJCksXoiIiEhSWLwQERGRpLB4ISIiIklh8UJERESSwuKFiIiIJIXFCxEREUkKixciIiKSFBYvREREJCkG2g6AiOhj9PrRCW2HQP/P2KaltkMgFXHmhYiIiCSFxQsRERFJCosXIiIikhQWL0RERCQpLF6IiIhIUli8EBERkaSweCEiIiJJYfFCREREksLihYiIiCSFxQsRERFJCosXIiIikhQWL0RERCQpLF6IiIhIUli8EBERkaSweCEiIiJJYfFCREREksLihYiIiCSFxQsRERFJCosXIiIikhQWL0RERCQpLF6IiIhIUli8EBERkaSweCEiIiJJYfFCREREksLihYiIiCSFxQsRERFJCosXIiIikhQWL0RERCQpLF6IiIhIUli8EBERkaSweCEiIiJJYfGiJmPj0mr3nTRxFE6f2oeUpBt49OAitm8LQM2a1fO0W71qIWZMn1iUMPMYMXwgbsWewYvnt3Hq5B40buQq6vjaUJRctGzRBDtDgxB37zyyMx+iW7eO+bYTOxfK7leKmA/d8To9Xe2+IaF70fPbEWjSvheatO+Fb74bhxOnI/O0+/mXhVi8MrgoYeaxafsedOg9EA3cuqH/0LG4dPWGqONrQ1GOC0C5z26xj4upU3yQnflQYbl86bho4xeFVosXPT096OvrF7oYGBhoM8QCTRg/Eu3dW6nVt1XLpli+PBift/RApy79YWhgiL/2bYSJibG8jZ6eHrp2cceePYfEChl9+nTD/F/9MHPWQjRu0gkXY65i/74NsLAwF20f2lCUXJiamiAm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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dmf.plot.dominance(strict=True);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally we can see how each of the alternatives relate to each other \n", "dominatnes with *FX* using `compare()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "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", "
CriteriaPerformance
ROECAPRI
AlternativesPETrueTrueTrue3
FXFalseFalseFalse0
EqualsFalseFalseFalse0
\n", "
" ], "text/plain": [ " Criteria Performance\n", " ROE CAP RI \n", "Alternatives PE True True True 3\n", " FX False False False 0\n", "Equals False False False 0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "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", "
CriteriaPerformance
ROECAPRI
AlternativesJNTrueFalseTrue2
FXFalseFalseFalse0
EqualsFalseTrueFalse1
\n", "
" ], "text/plain": [ " Criteria Performance\n", " ROE CAP RI \n", "Alternatives JN True False True 2\n", " FX False False False 0\n", "Equals False True False 1" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "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", "
CriteriaPerformance
ROECAPRI
AlternativesAATrueTrueTrue3
FXFalseFalseFalse0
EqualsFalseFalseFalse0
\n", "
" ], "text/plain": [ " Criteria Performance\n", " ROE CAP RI \n", "Alternatives AA True True True 3\n", " FX False False False 0\n", "Equals False False False 0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "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", "
CriteriaPerformance
ROECAPRI
AlternativesFNTrueTrueTrue3
FXFalseFalseFalse0
EqualsFalseFalseFalse0
\n", "
" ], "text/plain": [ " Criteria Performance\n", " ROE CAP RI \n", "Alternatives FN True True True 3\n", " FX False False False 0\n", "Equals False False False 0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "for dominant in dmf.dominance.dominators_of(\"FX\"):\n", " display(dmf.dominance.compare(dominant, 'FX'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Filter non-dominated alternatives\n", "\n", "Finally skcriteria offers a way to filter non-dominated alternatives,\n", "which it accepts as a parameter if you want to evaluate strict dominance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "flt = filters.FilterNonDominated(strict=True)\n", "flt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "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", "
ROE[▲ 1.0]CAP[▲ 1.0]RI[▼ 1.0]
PE7535
JN5426
AA5628
FN5830
\n", "
4 Alternatives x 3 Criteria\n", "
" ], "text/plain": [ " ROE[▲ 1.0] CAP[▲ 1.0] RI[▼ 1.0]\n", "PE 7 5 35\n", "JN 5 4 26\n", "AA 5 6 28\n", "FN 5 8 30\n", "[4 Alternatives x 3 Criteria]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "flt.transform(dmf)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Full expermient\n", "\n", "We can finally create a complete MCDA experiment that takes into account\n", "the in satisfaction and dominance analysis.\n", "\n", "The complete experiment would have the following steps\n", "\n", "1. Eliminate alternatives that do not yield at least 2% ($ROE >= $2).\n", "2. Eliminate dominated alternatives.\n", "3. Convert all criteria to maximize.\n", "4. The weights are scaled by the total sum.\n", "5. The matrix is scaled by the vector modulus.\n", "6. Apply [TOPSIS](https://en.wikipedia.org/wiki/TOPSIS).\n", "\n", "The most convenient way to do this is to use a pipeline." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "), ('filternondominated', ), ('negateminimize', ), ('sumscaler', ), ('vectorscaler', ), ('topsis', )]]>" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from skcriteria.preprocessing import scalers, invert_objectives\n", "from skcriteria.agg.similarity import TOPSIS\n", "from skcriteria.pipeline import mkpipe\n", "\n", "pipe = mkpipe(\n", " filters.FilterGE({\"ROE\": 2}),\n", " filters.FilterNonDominated(strict=True),\n", " invert_objectives.NegateMinimize(),\n", " scalers.SumScaler(target=\"weights\"),\n", " scalers.VectorScaler(target=\"matrix\"),\n", " TOPSIS(),\n", ")\n", "\n", "pipe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now apply the pipeline to the original data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
AlternativesPEJNAAFN
Rank3421
\n", "Method: TOPSIS\n", "
" ], "text/plain": [ "Alternatives PE JN AA FN\n", "Rank 3 4 2 1\n", "[Method: TOPSIS]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pipe.evaluate(dm)" ] } ], "metadata": { "celltoolbar": "Edit Metadata", "kernelspec": { "display_name": "skcriteria", "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.10.6" }, "vscode": { "interpreter": { "hash": "fe6a36dc40d389c224ef9449f75590504421cf6f9ca64f7a6ea7fe0c2b9a2828" } } }, "nbformat": 4, "nbformat_minor": 4 }