From 313dbd7c6f6a69229bf13434b1984cbde1053a86 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 5 Jun 2023 01:57:55 +0200 Subject: [PATCH 01/67] feat: added divine extractor --- demo/DemoClassification.ipynb | 406 +++++------------- psyke/__init__.py | 9 + psyke/extraction/hypercubic/__init__.py | 8 +- .../extraction/hypercubic/creepy/__init__.py | 6 +- .../extraction/hypercubic/divine/__init__.py | 70 +++ psyke/extraction/hypercubic/hypercube.py | 37 +- 6 files changed, 219 insertions(+), 317 deletions(-) create mode 100644 psyke/extraction/hypercubic/divine/__init__.py diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index dfb35697..6d076555 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -47,7 +47,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "id": "f8e46c49", "metadata": {}, "outputs": [], @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "id": "38d5afb0", "metadata": {}, "outputs": [], @@ -85,7 +85,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "id": "4f807185", "metadata": {}, "outputs": [ @@ -94,7 +94,7 @@ "text/plain": " target\n0 setosa\n1 setosa\n2 setosa\n3 setosa\n4 setosa\n.. ...\n145 virginica\n146 virginica\n147 virginica\n148 virginica\n149 virginica\n\n[150 rows x 1 columns]", "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
target
0setosa
1setosa
2setosa
3setosa
4setosa
......
145virginica
146virginica
147virginica
148virginica
149virginica
\n

150 rows × 1 columns

\n
" }, - "execution_count": 7, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -115,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "id": "7ac49b4e", "metadata": {}, "outputs": [ @@ -124,7 +124,7 @@ "text/plain": " SepalLength SepalWidth PetalLength PetalWidth iris\n0 5.1 3.5 1.4 0.2 setosa\n1 4.9 3.0 1.4 0.2 setosa\n2 4.7 3.2 1.3 0.2 setosa\n3 4.6 3.1 1.5 0.2 setosa\n4 5.0 3.6 1.4 0.2 setosa\n.. ... ... ... ... ...\n145 6.7 3.0 5.2 2.3 virginica\n146 6.3 2.5 5.0 1.9 virginica\n147 6.5 3.0 5.2 2.0 virginica\n148 6.2 3.4 5.4 2.3 virginica\n149 5.9 3.0 5.1 1.8 virginica\n\n[150 rows x 5 columns]", "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
SepalLengthSepalWidthPetalLengthPetalWidthiris
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa
..................
1456.73.05.22.3virginica
1466.32.55.01.9virginica
1476.53.05.22.0virginica
1486.23.45.42.3virginica
1495.93.05.11.8virginica
\n

150 rows × 5 columns

\n
" }, - "execution_count": 8, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "03fc5e2c", "metadata": {}, "outputs": [], @@ -165,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "id": "aa8a3128", "metadata": {}, "outputs": [ @@ -173,14 +173,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Accuracy: 1.00\n", - "F1: 1.00\n" + "Accuracy: 0.97\n", + "F1: 0.97\n" ] } ], "source": [ "#predictor = MLPClassifier(alpha=1, max_iter=1000)\n", - "predictor = KNeighborsClassifier(n_neighbors=5)\n", + "predictor = KNeighborsClassifier(n_neighbors=7)\n", "#predictor = DecisionTreeClassifier()\n", "predictor.fit(train.iloc[:, :-1], train.iloc[:, -1])\n", "print(f'Accuracy: {accuracy_score(predictor.predict(test.iloc[:, :-1]), test.iloc[:, -1]):.2f}')\n", @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "outputs": [], "source": [ "def print_scores(scores):\n", @@ -210,140 +210,136 @@ } }, { - "cell_type": "code", - "execution_count": 12, + "cell_type": "markdown", + "source": [ + "We create an extractor that uses the CART algorithm and we extract prolog rules from our trained KNN." + ], "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 14, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ITER performance (5 rules with 97.37% coverage):\n", - "Classification accuracy = 0.97 (data), 0.97 (BB)\n", - "F1 = 0.97 (data), 0.97 (BB)\n", + "CART performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", "\n", - "ITER extracted rules:\n", + "CART extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 3.17], PetalWidth in [0.09, 2.10].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [3.17, 5.82], PetalWidth in [0.09, 1.74].\n", + " PetalLength =< 2.6.\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 4.05], PetalWidth in [2.10, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [5.82, 6.90], PetalWidth in [0.09, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [4.05, 5.82], PetalWidth in [1.74, 2.50].\n" + " PetalLength =< 4.75.\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" ] } ], "source": [ - "it = Extractor.iter(predictor, min_update=0.15, min_examples=150, threshold=0.1, max_iterations=600, n_points=1)\n", - "theory_from_iter = it.extract(train)\n", - "scores, completeness = get_scores(it, test, predictor)\n", - "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "cart = Extractor.cart(predictor, simplify=True)\n", + "theory_from_cart = cart.extract(train)\n", + "scores, completeness = get_scores(cart, test, predictor)\n", + "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", - "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "We create a GridEx extractor to extract prolog rules from the same KNN." - ] - }, - { - "cell_type": "code", - "execution_count": 13, + "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, + } + }, + { + "cell_type": "code", + "execution_count": 16, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "GridEx performance (3 rules with 94.74% coverage):\n", - "Classification accuracy = 0.92 (data), 0.92 (BB)\n", - "F1 = 0.92 (data), 0.92 (BB)\n", + "DiViNE performance (3 rules with 73.68% coverage):\n", + "Classification accuracy = 0.96 (data), 1.00 (BB)\n", + "F1 = 0.96 (data), 1.00 (BB)\n", "\n", - "GridEx extracted rules:\n", + "DiViNE extracted rules:\n", "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 2.47].\n", + " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.21, 4.68].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.68, 6.90].\n" + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n" ] } ], "source": [ - "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", - "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "divine = Extractor.divine(predictor, k=5, patience=15)\n", + "theory_from_divine = divine.extract(train)\n", + "scores, completeness = get_scores(divine, test, predictor)\n", + "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ] + "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "it = Extractor.iter(predictor, min_update=0.15, min_examples=150, threshold=0.1, max_iterations=600, n_points=1)\n", + "theory_from_iter = it.extract(train)\n", + "scores, completeness = get_scores(it, test, predictor)\n", + "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "markdown", + "source": [ + "We create a GridEx extractor to extract prolog rules from the same KNN." + ], "metadata": { "collapsed": false - }, - "source": [ - "We create an extractor that uses the CART algorithm and we extract prolog rules from our trained KNN." - ] + } }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, + "outputs": [], + "source": [ + "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", + "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", + "theory_from_gridEx = gridEx.extract(train)\n", + "scores, completeness = get_scores(gridEx, test, predictor)\n", + "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 0.97 (BB)\n", - "F1 = 0.97 (data), 0.97 (BB)\n", - "\n", - "CART extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength =< 2.6.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength =< 4.75.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ] + } }, { "cell_type": "markdown", @@ -359,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "outputs": [], "source": [ "def print_clustering_scores(scores):\n", @@ -389,53 +385,8 @@ }, { "cell_type": "code", - "execution_count": 16, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.ExACT. Depth: 1. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.02. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.06. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.11. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.15. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.20. Predictive loss = 0.30, 2 rules\n", - "\n", - "Algorithm.ExACT. Depth: 2. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.02. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.06. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.11. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.15. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.20. Predictive loss = 0.27, 3 rules\n", - "\n", - "****************************************\n", - "* Best Algorithm.ExACT\n", - "****************************************\n", - "* Predictive loss = 0.27, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best Predictive loss\n", - "****************************************\n", - "* Predictive loss = 0.27, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best N rules\n", - "****************************************\n", - "* Predictive loss = 0.30, 2 rules\n", - "* Threshold = 1.00\n", - "* Depth = 2\n", - "****************************************\n", - "\n" - ] - } - ], + "execution_count": null, + "outputs": [], "source": [ "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.ExACT, output=Target.CLASSIFICATION,\n", " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", @@ -451,43 +402,14 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ExACT performance (3 clusters with 97.37% coverage):\n", - "ARI = 0.53\n", - "AMI = 0.62\n", - "V-measure = 0.64\n", - "FMI = 0.69\n", - "Classification accuracy = 0.78\n", - "\n", - "Output is virginica if:\n", - " SepalLength is in [4.90, 7.70]\n", - " SepalWidth is in [2.20, 3.80]\n", - " PetalLength is in [4.50, 6.90]\n", - " PetalWidth is in [1.40, 2.50]\n", - "Output is versicolor if:\n", - " SepalLength is in [4.90, 7.70]\n", - " SepalWidth is in [2.00, 3.80]\n", - " PetalLength is in [3.30, 6.90]\n", - " PetalWidth is in [1.00, 2.50]\n", - "Output is setosa if:\n", - " SepalLength is in [4.30, 7.70]\n", - " SepalWidth is in [2.00, 4.40]\n", - " PetalLength is in [1.00, 6.90]\n", - " PetalWidth is in [0.10, 2.50]\n" - ] - } - ], + "outputs": [], "source": [ "exact = Clustering.exact(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", "exact.fit(train)\n", @@ -502,32 +424,14 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.95 (BB)\n", - "F1 = 0.95 (data), 0.95 (BB)\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.79, 6.90].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.29, 6.90].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa).\n" - ] - } - ], + "outputs": [], "source": [ "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", " ranks=ranked, ignore_threshold=.99, clustering=Clustering.exact)\n", @@ -540,53 +444,8 @@ }, { "cell_type": "code", - "execution_count": 19, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.CREAM. Depth: 1. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.02. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.06. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.11. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.15. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.20. Predictive loss = 0.30, 2 rules\n", - "\n", - "Algorithm.CREAM. Depth: 2. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.02. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.06. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.11. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.15. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.20. Predictive loss = 0.07, 3 rules\n", - "\n", - "****************************************\n", - "* Best Algorithm.CREAM\n", - "****************************************\n", - "* Predictive loss = 0.07, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best Predictive loss\n", - "****************************************\n", - "* Predictive loss = 0.07, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best N rules\n", - "****************************************\n", - "* Predictive loss = 0.30, 2 rules\n", - "* Threshold = 1.00\n", - "* Depth = 2\n", - "****************************************\n", - "\n" - ] - } - ], + "execution_count": null, + "outputs": [], "source": [ "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.CREAM, output=Target.CLASSIFICATION,\n", " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", @@ -602,43 +461,14 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CREAM performance (3 clusters with 97.37% coverage):\n", - "ARI = 0.85\n", - "AMI = 0.82\n", - "V-measure = 0.83\n", - "FMI = 0.90\n", - "Classification accuracy = 0.95\n", - "\n", - "Output is setosa if:\n", - " SepalLength is in [4.30, 5.70]\n", - " SepalWidth is in [2.30, 4.40]\n", - " PetalLength is in [1.00, 1.90]\n", - " PetalWidth is in [0.10, 0.60]\n", - "Output is versicolor if:\n", - " SepalLength is in [4.90, 7.00]\n", - " SepalWidth is in [2.00, 3.40]\n", - " PetalLength is in [3.30, 5.10]\n", - " PetalWidth is in [1.00, 1.80]\n", - "Output is virginica if:\n", - " SepalLength is in [4.30, 7.70]\n", - " SepalWidth is in [2.00, 4.40]\n", - " PetalLength is in [1.00, 6.90]\n", - " PetalWidth is in [0.10, 2.50]\n" - ] - } - ], + "outputs": [], "source": [ "cream = Clustering.cream(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", "cream.fit(train)\n", @@ -653,32 +483,14 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.95 (BB)\n", - "F1 = 0.95 (data), 0.95 (BB)\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 1.90].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.29, 5.00].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" - ] - } - ], + "outputs": [], "source": [ "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", " ranks=ranked, ignore_threshold=.99, clustering=Clustering.cream)\n", diff --git a/psyke/__init__.py b/psyke/__init__.py index eb71036c..e8d1564a 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -218,6 +218,15 @@ def cart(predictor, max_depth: int = 3, max_leaves: int = 3, return Cart(predictor, max_depth, max_leaves, discretization=discretization, normalization=normalization, simplify=simplify) + @staticmethod + def divine(predictor, k: int = 5, patience: int = 15, + discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: + """ + Creates a new DiViNE extractor. + """ + from psyke.extraction.hypercubic.divine import DiViNE + return DiViNE(predictor, k=k, patience=patience, discretization=discretization, normalization=normalization) + @staticmethod def iter(predictor, min_update: float = 0.1, n_points: int = 1, max_iterations: int = 600, min_examples: int = 250, threshold: float = 0.1, fill_gaps: bool = True, normalization: dict[str, tuple[float, float]] = None, diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 7d7783a5..e0233eb7 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -17,8 +17,8 @@ class HyperCubePredictor: - def __init__(self, cubes=[], output=Target.CONSTANT, normalization=None): - self._hypercubes = cubes + def __init__(self, output=Target.CONSTANT, normalization=None): + self._hypercubes = [] self._output = output self.normalization = normalization @@ -50,8 +50,8 @@ def _get_cube_output(cube, data: dict[str, float]) -> float: class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC): - def __init__(self, predictor, output, normalization): - PedagogicalExtractor.__init__(self, predictor, normalization=normalization) + def __init__(self, predictor, output, discretization=None, normalization=None): + PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) HyperCubePredictor.__init__(self, output=output, normalization=normalization) def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: diff --git a/psyke/extraction/hypercubic/creepy/__init__.py b/psyke/extraction/hypercubic/creepy/__init__.py index 8bdba96f..eb4a9584 100644 --- a/psyke/extraction/hypercubic/creepy/__init__.py +++ b/psyke/extraction/hypercubic/creepy/__init__.py @@ -13,16 +13,16 @@ from psyke.utils import Target -class CReEPy(HyperCubeExtractor, ABC): +class CReEPy(HyperCubeExtractor): """ Explanator implementing CReEPy algorithm. """ def __init__(self, predictor, depth: int, error_threshold: float, output: Target = Target.CONSTANT, gauss_components: int = 5, ranks: list[(str, float)] = [], ignore_threshold: float = 0.0, - normalization=None, clustering=Clustering.exact): + discretization=None, normalization=None, clustering=Clustering.exact): super().__init__(predictor, Target.CLASSIFICATION if isinstance(predictor, ClassifierMixin) else output, - normalization) + discretization, normalization) self.clustering = clustering(depth, error_threshold, self._output, gauss_components) self.ranks = ranks self.ignore_threshold = ignore_threshold diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py new file mode 100644 index 00000000..fe1735d9 --- /dev/null +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -0,0 +1,70 @@ +import numpy as np +import pandas as pd +from tuprolog.theory import Theory + +from psyke import Target +from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube + +from sklearn.neighbors import BallTree + +from psyke.extraction.hypercubic.hypercube import Point, GenericCube + + +class DiViNE(HyperCubeExtractor): + """ + Explanator implementing DiViNE algorithm. + """ + + def __init__(self, predictor, k: int = 5, patience: int = 15, discretization=None, normalization=None): + super().__init__(predictor, Target.CLASSIFICATION, discretization, normalization) + self.k = k + self.patience = patience + + @staticmethod + def __pop(data: pd.DataFrame, idx: int = None) -> (Point, pd.DataFrame): + if idx is None: + idx = data.sample(1).index.values[0] + t = data.T + return DiViNE.__to_point(t.pop(idx)), t.T.reset_index(drop=True) + + @staticmethod + def __to_point(instance) -> Point: + point = Point(instance.index.values, instance.values) + return point + + def __to_cube(self, point: Point) -> GenericCube: + cube = HyperCube.cube_from_point(point.dimensions, self._output) + cube._output = list(point.dimensions.values())[-1] + return cube + + def __clean(self, data: pd.DataFrame) -> pd.DataFrame: + tree = BallTree(data.iloc[:, :-1], leaf_size=2) + _, idx = tree.query(data.iloc[:, :-1], k=self.k) + # how many output classes are associated with the k neighbors + count = np.array(list(map(lambda indices: len(data.iloc[indices].iloc[:, -1].unique()), idx))) + # instances with neighbors of different classes are discarded + return data[count == 1] + + def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: + data = self.__clean(dataframe) + + while len(data) > 0: + discarded = [] + patience = self.patience + point, data = self.__pop(data) + cube = self.__to_cube(point) + + while patience > 0 and len(data) > 0: + tree = BallTree(data.iloc[:, :-1], leaf_size=2) + _, idx = tree.query([list(cube.center().dimensions.values())], k=1) + other, data = DiViNE.__pop(data, idx[0][-1]) + if cube.output == list(other.dimensions.values())[-1]: + cube = cube.merge_with_point(other) + data = data[~(cube.filter_indices(data.iloc[:, :-1]))].reset_index(drop=True) + else: + patience -= 1 + discarded.append(other) + self._hypercubes.append(cube) + if len(discarded) > 0: + data = pd.concat([data] + [d.to_dataframe() for d in discarded]).reset_index(drop=True) + return self._create_theory(dataframe, sort) diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 7c8d2d2b..f122d104 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -30,25 +30,28 @@ class Point: EPSILON = get_default_precision() - def __init__(self, dimensions: list[str], values: list[float]): + def __init__(self, dimensions: list[str], values: list[float | str]): self._dimensions = {dimension: value for (dimension, value) in zip(dimensions, values)} - def __getitem__(self, feature: str) -> float: + def __getitem__(self, feature: str) -> float | str: if feature in self._dimensions.keys(): return self._dimensions[feature] else: raise FeatureNotFoundException(feature) - def __setitem__(self, key: str, value: float) -> None: + def __setitem__(self, key: str, value: float | str) -> None: self._dimensions[key] = value def __eq__(self, other: Point) -> bool: return all([abs(self[dimension] - other[dimension]) < Point.EPSILON for dimension in self._dimensions]) @property - def dimensions(self) -> dict[str, float]: + def dimensions(self) -> dict[str, float | str]: return self._dimensions + def to_dataframe(self) -> pd.DataFrame: + return pd.DataFrame(data=[self.dimensions.values()], columns=list(self.dimensions.keys())) + class HyperCube: """ @@ -59,7 +62,7 @@ class HyperCube: INT_PRECISION = get_int_precision() def __init__(self, dimension: dict[str, tuple[float, float]] = None, limits: set[Limit] = None, - output: float | LinearRegression = 0.0): + output: float | LinearRegression | str = 0.0): self._dimensions = self._fit_dimension(dimension) if dimension is not None else {} self._limits = limits if limits is not None else set() self._output = output @@ -101,7 +104,7 @@ def limit_count(self) -> int: return len(self._limits) @property - def output(self) -> float | LinearRegression: + def output(self) -> float | str | LinearRegression: return self._output @property @@ -128,9 +131,8 @@ def filter_indices(self, dataset: pd.DataFrame) -> ndarray: def _filter_dataframe(self, dataset: pd.DataFrame) -> pd.DataFrame: return dataset[self.filter_indices(dataset)] - def _zip_dimensions(self, hypercube: HyperCube) -> list[ZippedDimension]: - return [ZippedDimension(dimension, self[dimension], hypercube[dimension]) - for dimension in self._dimensions.keys()] + def _zip_dimensions(self, other: HyperCube) -> list[ZippedDimension]: + return [ZippedDimension(dimension, self[dimension], other[dimension]) for dimension in self._dimensions.keys()] def add_limit(self, limit_or_feature: Limit | str, direction: str = None) -> None: if isinstance(limit_or_feature, Limit): @@ -202,7 +204,7 @@ def _create_tuple(self, generator: Random) -> dict: return {k: generator.uniform(self.get_first(k), self.get_second(k)) for k in self._dimensions.keys()} @staticmethod - def cube_from_point(point: dict, output=None) -> GenericCube: + def cube_from_point(point: dict[str, float], output=None) -> GenericCube: if output is Target.CLASSIFICATION: return ClassificationCube({k: (v, v) for k, v in list(point.items())[:-1]}) if output is Target.REGRESSION: @@ -276,6 +278,15 @@ def merge_along_dimension(self, cube: HyperCube, feature: str) -> HyperCube: new_cube.update_dimension(feature, (min(a1, a2), max(b1, b2))) return new_cube + def merge(self, other: HyperCube) -> HyperCube: + new_cube = self.copy() + for dimension in self.dimensions.keys(): + new_cube = new_cube.merge_along_dimension(other, dimension) + return new_cube + + def merge_with_point(self, other: Point) -> HyperCube: + return self.merge(HyperCube.cube_from_point(other.dimensions)) + # TODO: maybe two different methods are more readable and easier to debug def overlap(self, hypercubes: Iterable[HyperCube] | HyperCube) -> HyperCube | bool | None: if isinstance(hypercubes, Iterable): @@ -335,8 +346,8 @@ def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, norma class ClassificationCube(HyperCube): - def __init__(self, dimension: dict[str, tuple] = None): - super().__init__(dimension=dimension) + def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None, output: str = ""): + super().__init__(dimension=dimension, limits=limits, output=output) def update(self, dataset: pd.DataFrame, predictor) -> None: filtered = self._filter_dataframe(dataset.iloc[:, :-1]) @@ -346,7 +357,7 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: self._diversity = 1 - sum(prediction == self.output for prediction in predictions) / len(filtered) def copy(self) -> ClassificationCube: - return ClassificationCube(self.dimensions.copy()) + return ClassificationCube(self.dimensions.copy(), self._limits.copy(), self._output) class ClosedCube(HyperCube): From e62b259e7a3f7ae50e73e3d7726ec61f91fec1d8 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 5 Jun 2023 02:56:36 +0200 Subject: [PATCH 02/67] feat(divine): added option for vicinity to corners --- demo/DemoClassification.ipynb | 80 +++++++++++++++++-- psyke/__init__.py | 5 +- .../extraction/hypercubic/divine/__init__.py | 31 +++++-- 3 files changed, 101 insertions(+), 15 deletions(-) diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 6d076555..8277026d 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -165,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 7, "id": "aa8a3128", "metadata": {}, "outputs": [ @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 8, "outputs": [], "source": [ "def print_scores(scores):\n", @@ -220,7 +220,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 9, "outputs": [ { "name": "stdout", @@ -257,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 10, "outputs": [ { "name": "stdout", @@ -269,17 +269,55 @@ "\n", "DiViNE extracted rules:\n", "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " SepalLength in [5.2, 7.0], SepalWidth in [2.2, 3.2], PetalLength in [3.5, 4.9], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" + ] + } + ], + "source": [ + "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=True)\n", + "theory_from_divine = divine.extract(train)\n", + "scores, completeness = get_scores(divine, test, predictor)\n", + "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DiViNE performance (3 rules with 73.68% coverage):\n", + "Classification accuracy = 0.96 (data), 1.00 (BB)\n", + "F1 = 0.96 (data), 1.00 (BB)\n", + "\n", + "DiViNE extracted rules:\n", + "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n" + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" ] } ], "source": [ - "divine = Extractor.divine(predictor, k=5, patience=15)\n", + "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=False)\n", "theory_from_divine = divine.extract(train)\n", "scores, completeness = get_scores(divine, test, predictor)\n", "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", @@ -293,6 +331,32 @@ } } }, + { + "cell_type": "code", + "execution_count": 12, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'dfdgh' is not defined", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mNameError\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_21096/3072512347.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[1;32m----> 1\u001B[1;33m \u001B[0mdfdgh\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;31mNameError\u001B[0m: name 'dfdgh' is not defined" + ] + } + ], + "source": [ + "dfdgh" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": null, diff --git a/psyke/__init__.py b/psyke/__init__.py index e8d1564a..7f980a5f 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -219,13 +219,14 @@ def cart(predictor, max_depth: int = 3, max_leaves: int = 3, simplify=simplify) @staticmethod - def divine(predictor, k: int = 5, patience: int = 15, + def divine(predictor, k: int = 5, patience: int = 15, close_to_center: bool = True, discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: """ Creates a new DiViNE extractor. """ from psyke.extraction.hypercubic.divine import DiViNE - return DiViNE(predictor, k=k, patience=patience, discretization=discretization, normalization=normalization) + return DiViNE(predictor, k=k, patience=patience, close_to_center=close_to_center, + discretization=discretization, normalization=normalization) @staticmethod def iter(predictor, min_update: float = 0.1, n_points: int = 1, max_iterations: int = 600, min_examples: int = 250, diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index fe1735d9..ff023fcb 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -15,10 +15,12 @@ class DiViNE(HyperCubeExtractor): Explanator implementing DiViNE algorithm. """ - def __init__(self, predictor, k: int = 5, patience: int = 15, discretization=None, normalization=None): + def __init__(self, predictor, k: int = 5, patience: int = 15, close_to_center: bool = True, + discretization=None, normalization=None): super().__init__(predictor, Target.CLASSIFICATION, discretization, normalization) self.k = k self.patience = patience + self.vicinity_function = DiViNE.__closest_to_center if close_to_center else DiViNE.__closest_to_corners @staticmethod def __pop(data: pd.DataFrame, idx: int = None) -> (Point, pd.DataFrame): @@ -45,6 +47,24 @@ def __clean(self, data: pd.DataFrame) -> pd.DataFrame: # instances with neighbors of different classes are discarded return data[count == 1] + def __sort_cubes(self): + cubes = [(cube.diversity, i, cube) for i, cube in enumerate(self._hypercubes)] + cubes.sort() + self._hypercubes = [cube[2] for cube in cubes] + + def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFrame): + tree = BallTree(data.iloc[:, :-1], leaf_size=2) + return DiViNE.__pop(data,self.vicinity_function(tree, cube)) + + @staticmethod + def __closest_to_center(tree: BallTree, cube: GenericCube): + return tree.query([list(cube.center().dimensions.values())], k=1)[1][0][-1] + + @staticmethod + def __closest_to_corners(tree: BallTree, cube: GenericCube): + distance, idx = tree.query([list(point.dimensions.values()) for point in cube.corners()], k=1) + return idx[np.argmin(distance)][-1] + def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: data = self.__clean(dataframe) @@ -55,16 +75,17 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort cube = self.__to_cube(point) while patience > 0 and len(data) > 0: - tree = BallTree(data.iloc[:, :-1], leaf_size=2) - _, idx = tree.query([list(cube.center().dimensions.values())], k=1) - other, data = DiViNE.__pop(data, idx[0][-1]) + other, data = self.__closest(data, cube) if cube.output == list(other.dimensions.values())[-1]: cube = cube.merge_with_point(other) data = data[~(cube.filter_indices(data.iloc[:, :-1]))].reset_index(drop=True) else: patience -= 1 discarded.append(other) - self._hypercubes.append(cube) + if cube.volume() > 0: + cube.update(dataframe, self.predictor) + self._hypercubes.append(cube) if len(discarded) > 0: data = pd.concat([data] + [d.to_dataframe() for d in discarded]).reset_index(drop=True) + self.__sort_cubes() return self._create_theory(dataframe, sort) From 74ae609adb26714aacbf24ddfd790197dc27ecf4 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 11 Jun 2023 03:28:04 +0200 Subject: [PATCH 03/67] feat(hypercube): density-based vicinity --- .gitignore | 3 +- demo/DemoClassification.ipynb | 165 ++++++++++++++---- psyke/__init__.py | 11 +- psyke/extraction/hypercubic/__init__.py | 49 +++++- .../extraction/hypercubic/divine/__init__.py | 5 +- psyke/extraction/hypercubic/hypercube.py | 39 ++++- psyke/extraction/hypercubic/iter/__init__.py | 1 - .../extraction/hypercubic/test_hypercube.py | 2 +- 8 files changed, 225 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index fc613289..cc76eb82 100644 --- a/.gitignore +++ b/.gitignore @@ -159,4 +159,5 @@ dmypy.json # Local stuff dummy/ tmp_model/ -plots/ \ No newline at end of file +plots/ +demo/ \ No newline at end of file diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 8277026d..80848efe 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -198,8 +198,8 @@ " f'F1 = {scores[EvaluableModel.ClassificationScore.F1][0]:.2f} (data), '\n", " f'{scores[EvaluableModel.ClassificationScore.F1][1]:.2f} (BB)')\n", "\n", - "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, EvaluableModel.Task.CLASSIFICATION,\n", + "def get_scores(extractor, test, predictor, brute=False, criterion='density', n=2):\n", + " return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n", " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" ], "metadata": { @@ -267,10 +267,14 @@ "Classification accuracy = 0.96 (data), 1.00 (BB)\n", "F1 = 0.96 (data), 1.00 (BB)\n", "\n", + "DiViNE brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", "DiViNE extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [5.2, 7.0], SepalWidth in [2.2, 3.2], PetalLength in [3.5, 4.9], PetalWidth in [1.0, 1.5].\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", @@ -284,6 +288,11 @@ "scores, completeness = get_scores(divine, test, predictor)\n", "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(divine, test, predictor, True, 'density')\n", + "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" ], "metadata": { @@ -295,20 +304,24 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 34, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "DiViNE performance (3 rules with 73.68% coverage):\n", + "DiViNE performance (3 rules with 71.05% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", "F1 = 0.96 (data), 1.00 (BB)\n", "\n", + "DiViNE brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", "DiViNE extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.7], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", @@ -322,6 +335,11 @@ "scores, completeness = get_scores(divine, test, predictor)\n", "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(divine, test, predictor, True)\n", + "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" ], "metadata": { @@ -333,40 +351,43 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 42, "outputs": [ { - "ename": "NameError", - "evalue": "name 'dfdgh' is not defined", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mNameError\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_21096/3072512347.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[1;32m----> 1\u001B[1;33m \u001B[0mdfdgh\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;31mNameError\u001B[0m: name 'dfdgh' is not defined" + "name": "stdout", + "output_type": "stream", + "text": [ + "ITER performance (3 rules with 97.37% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", + "ITER brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", + "ITER extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 2.58], PetalWidth in [0.09, 2.50].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [2.58, 4.94], PetalWidth in [0.09, 2.50].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [4.94, 6.90], PetalWidth in [0.09, 2.50].\n" ] } ], "source": [ - "dfdgh" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "it = Extractor.iter(predictor, min_update=0.15, min_examples=150, threshold=0.1, max_iterations=600, n_points=1)\n", + "it = Extractor.iter(predictor, min_update=0.2, min_examples=150, threshold=0.1,\n", + " max_iterations=100, n_points=1, fill_gaps=True)\n", "theory_from_iter = it.extract(train)\n", "scores, completeness = get_scores(it, test, predictor)\n", "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(it, test, predictor, True)\n", + "print(f'\\nITER brute performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" ], "metadata": { @@ -387,8 +408,83 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 40, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GridEx performance (5 rules with 94.74% coverage):\n", + "Classification accuracy = 0.94 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", + "\n", + "GridEx brute performance (5 rules with 100.00% coverage):\n", + "Classification accuracy = 0.95 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", + "\n", + "GridEx extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [4.54, 5.72], PetalWidth in [1.06, 1.54].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " PetalLength in [0.99, 2.17], PetalWidth in [0.09, 1.06].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [2.17, 4.54], PetalWidth in [0.57, 1.06].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [3.36, 4.54], PetalWidth in [1.06, 2.02].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " PetalLength in [4.54, 6.90], PetalWidth in [1.54, 2.50].\n" + ] + } + ], + "source": [ + "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", + "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.7, 5)])), threshold=.1, min_examples=1)\n", + "theory_from_gridEx = gridEx.extract(train)\n", + "scores, completeness = get_scores(gridEx, test, predictor)\n", + "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(gridEx, test, predictor, True)\n", + "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", + "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 41, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GridEx performance (3 rules with 94.74% coverage):\n", + "Classification accuracy = 0.92 (data), 0.94 (BB)\n", + "F1 = 0.92 (data), 0.95 (BB)\n", + "\n", + "GridEx brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.92 (data), 0.95 (BB)\n", + "F1 = 0.92 (data), 0.95 (BB)\n", + "\n", + "GridEx extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " PetalLength in [0.99, 2.47].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [3.21, 4.68].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " PetalLength in [4.68, 6.90].\n" + ] + } + ], "source": [ "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", @@ -396,6 +492,11 @@ "scores, completeness = get_scores(gridEx, test, predictor)\n", "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(gridEx, test, predictor, True)\n", + "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" ], "metadata": { diff --git a/psyke/__init__.py b/psyke/__init__.py index 7f980a5f..3e7ae087 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -62,9 +62,12 @@ def predict(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None) -> It ys = [inverse_mapping[y] for y in ys] return ys - def _predict(self, dataframe: pd.DataFrame) -> Iterable: + def _predict(self, dataframe: pd.DataFrame, criterion: str = 'perimeter') -> Iterable: raise NotImplementedError('predict') + def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + raise NotImplementedError('brute_predict') + def unscale(self, values, name): if self.normalization is None or isinstance(values, LinearRegression): return values @@ -76,9 +79,13 @@ def unscale(self, values, name): return values def score(self, dataframe: pd.DataFrame, predictor=None, fidelity: bool = False, completeness: bool = True, + brute: bool = False, criterion: str = 'corners', n: int = 2, task: EvaluableModel.Task = Task.CLASSIFICATION, scoring_function: Iterable[EvaluableModel.Score] = [ClassificationScore.ACCURACY]): - extracted = np.array(self.predict(dataframe.iloc[:, :-1])) + extracted = np.array( + self.predict(dataframe.iloc[:, :-1]) if not brute else + self.brute_predict(dataframe.iloc[:, :-1], criterion, n) + ) idx = [prediction is not None for prediction in extracted] y_extracted = extracted[idx] true = [dataframe.iloc[idx, -1]] diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index e0233eb7..fe235c1d 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -10,10 +10,12 @@ from tuprolog.core import Var, Struct, clause from tuprolog.theory import Theory, mutable_theory from psyke import logger, PedagogicalExtractor -from psyke.extraction.hypercubic.hypercube import HyperCube, RegressionCube, ClassificationCube, ClosedCube +from psyke.extraction.hypercubic.hypercube import HyperCube, RegressionCube, ClassificationCube, ClosedCube, Point, \ + GenericCube from psyke.utils.logic import create_variable_list, create_head, to_var, Simplifier from psyke.utils import Target, get_int_precision from psyke.extraction.hypercubic.strategy import Strategy, FixedStrategy +from sklearn.neighbors import BallTree class HyperCubePredictor: @@ -23,12 +25,49 @@ def __init__(self, output=Target.CONSTANT, normalization=None): self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: - return np.array([self._predict_from_cubes(dict(row.to_dict())) for _, row in dataframe.iterrows()]) + return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - def _predict_from_cubes(self, data: dict[str, float]) -> float | None: - data = {k: v for k, v in data.items()} + def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + predictions = self._predict(dataframe) + idx = [prediction is None for prediction in predictions] + + tree, mapping = self._create_brute_tree(dataframe, criterion, n) + + predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( + row.to_dict(), tree, mapping + ) for _, row in dataframe[idx].iterrows()]) + return predictions + + @staticmethod + def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, + mapping: dict[int, GenericCube]) -> float | str: + idx = tree.query([list(row.values())], k=1)[1][0][0] + return HyperCubePredictor._get_cube_output(mapping[idx], row) + + def _create_brute_tree(self, dataframe: pd.DataFrame, + criterion: str = 'center', n: int = 2) -> (BallTree, dict[int, GenericCube]): + points = None + if criterion == 'center': + points = [(cube.center(), i, cube) for i, cube in enumerate(self._hypercubes)] + elif criterion == 'density': + points = [(cube.filter_dataframe(dataframe).describe().loc['mean'], i, cube) + for i, cube in enumerate(self._hypercubes)] + points = [(Point(df.index.values, df.values), i, cube) for df, i, cube in points] + elif criterion == 'corner': + points = [(corner, cube) for cube in self._hypercubes for corner in cube.corners()] + points = [(point[0], i, point[1]) for i, point in enumerate(points)] + elif criterion == 'perimeter': + points = [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] + points = [(point[0], i, point[1]) for i, point in enumerate(points)] + else: + raise NotImplementedError("'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density']") + + return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True), leaf_size=2), \ + {point[1]: point[2] for point in points} + + def _predict_from_cubes(self, data: dict[str, float]) -> float | str | None: for cube in self._hypercubes: - if cube.__contains__(data): + if data in cube: if self._output == Target.CLASSIFICATION: return HyperCubePredictor._get_cube_output(cube, data) else: diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index ff023fcb..fff183bb 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -3,12 +3,11 @@ from tuprolog.theory import Theory from psyke import Target -from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube +from psyke.extraction.hypercubic import HyperCubeExtractor +from psyke.extraction.hypercubic.hypercube import Point, GenericCube, HyperCube from sklearn.neighbors import BallTree -from psyke.extraction.hypercubic.hypercube import Point, GenericCube - class DiViNE(HyperCubeExtractor): """ diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index f122d104..777c5e61 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -52,6 +52,9 @@ def dimensions(self) -> dict[str, float | str]: def to_dataframe(self) -> pd.DataFrame: return pd.DataFrame(data=[self.dimensions.values()], columns=list(self.dimensions.keys())) + def copy(self) -> Point: + return Point(list(self._dimensions.keys()), list(self._dimensions.values())) + class HyperCube: """ @@ -128,7 +131,7 @@ def filter_indices(self, dataset: pd.DataFrame) -> ndarray: ds = dataset.to_numpy(copy=True) return np.all((v[:, 0] <= ds) & (ds < v[:, 1]), axis=1) - def _filter_dataframe(self, dataset: pd.DataFrame) -> pd.DataFrame: + def filter_dataframe(self, dataset: pd.DataFrame) -> pd.DataFrame: return dataset[self.filter_indices(dataset)] def _zip_dimensions(self, other: HyperCube) -> list[ZippedDimension]: @@ -169,7 +172,7 @@ def copy(self) -> HyperCube: return HyperCube(self.dimensions.copy(), self._limits.copy(), self.output) def count(self, dataset: pd.DataFrame) -> int: - return self._filter_dataframe(dataset.iloc[:, :-1]).shape[0] + return self.filter_dataframe(dataset.iloc[:, :-1]).shape[0] def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, normalization=None) -> Iterable[Struct]: dimensions = dict(self.dimensions) @@ -260,6 +263,32 @@ def corners(self) -> Iterable[Point]: Point(list(self._dimensions.keys()), values) for values in itertools.product(*self._dimensions.values()) ] + def perimeter_samples(self, n: int = 5) -> Iterable[Point]: + def duplicate(point: Point, feature: str) -> Iterable[Point]: + new_point_a = point.copy() + new_point_b = point.copy() + new_point_a[feature] = self.get_first(feature) + new_point_b[feature] = self.get_second(feature) + return [new_point_a, new_point_b] + + def split(point: Point, feature: str, n: int): + points = [] + for value in np.linspace(self.get_first(feature), self.get_second(feature), n): + new_point = point.copy() + new_point[feature] = value + points.append(new_point) + return points + + points = [] + for primary in self._dimensions: + new_points = [Point([], [])] + for secondary in self._dimensions: + if primary != secondary: + new_points = np.array([duplicate(point, secondary) for point in new_points]).flatten() + new_points = np.array([split(point, primary, n) for point in new_points]).flatten() + points = points + list(new_points) + return points + def is_adjacent(self, cube: HyperCube) -> str | None: adjacent = None for (feature, [a1, b1]) in self._dimensions.items(): @@ -309,7 +338,7 @@ def update_dimension(self, feature: str, lower: float | tuple[float, float], upp self.update_dimension(feature, (lower, upper)) def update(self, dataset: pd.DataFrame, predictor) -> None: - filtered = self._filter_dataframe(dataset.iloc[:, :-1]) + filtered = self.filter_dataframe(dataset.iloc[:, :-1]) predictions = predictor.predict(filtered) self._output = np.mean(predictions) self._diversity = np.std(predictions) @@ -324,7 +353,7 @@ def __init__(self, dimension: dict[str, tuple] = None): super().__init__(dimension=dimension, output=LinearRegression()) def update(self, dataset: pd.DataFrame, predictor) -> None: - filtered = self._filter_dataframe(dataset.iloc[:, :-1]) + filtered = self.filter_dataframe(dataset.iloc[:, :-1]) if len(filtered > 0): predictions = predictor.predict(filtered) self._output.fit(filtered, predictions) @@ -350,7 +379,7 @@ def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None super().__init__(dimension=dimension, limits=limits, output=output) def update(self, dataset: pd.DataFrame, predictor) -> None: - filtered = self._filter_dataframe(dataset.iloc[:, :-1]) + filtered = self.filter_dataframe(dataset.iloc[:, :-1]) if len(filtered > 0): predictions = predictor.predict(filtered) self._output = mode(predictions) diff --git a/psyke/extraction/hypercubic/iter/__init__.py b/psyke/extraction/hypercubic/iter/__init__.py index 7f2ca76d..1e6d8237 100644 --- a/psyke/extraction/hypercubic/iter/__init__.py +++ b/psyke/extraction/hypercubic/iter/__init__.py @@ -5,7 +5,6 @@ import pandas as pd from sklearn.base import ClassifierMixin from tuprolog.theory import Theory -from psyke import PedagogicalExtractor from psyke.extraction.hypercubic import HyperCube, HyperCubeExtractor from psyke.extraction.hypercubic.hypercube import GenericCube from psyke.extraction.hypercubic.utils import MinUpdate, Expansion diff --git a/test/psyke/extraction/hypercubic/test_hypercube.py b/test/psyke/extraction/hypercubic/test_hypercube.py index d1ee273a..b241029c 100644 --- a/test/psyke/extraction/hypercubic/test_hypercube.py +++ b/test/psyke/extraction/hypercubic/test_hypercube.py @@ -162,7 +162,7 @@ def test_filter_dataframe(self): expected = (self.dataset.X >= self.x[0]) & (self.dataset.X < self.x[1]) & \ (self.dataset.Y >= self.y[0]) & (self.dataset.Y < self.y[1]) expected = self.dataset[expected].iloc[:, :-1] - filtered = self.cube._filter_dataframe(self.dataset.iloc[:, :-1]) + filtered = self.cube.filter_dataframe(self.dataset.iloc[:, :-1]) self.assertTrue(all(expected == filtered)) def test_update(self): From 2ceaf06a0bf5cc8c539697573dd3720c62b9501a Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Fri, 16 Jun 2023 22:03:46 +0200 Subject: [PATCH 04/67] feat(divine): added vicinity to perimeter and barycenter --- .gitignore | 2 +- psyke/extraction/hypercubic/__init__.py | 23 +++---- .../extraction/hypercubic/divine/__init__.py | 6 +- psyke/extraction/hypercubic/hypercube.py | 16 ++++- psyke/utils/plot.py | 69 ++++++++++++++++++- requirements.txt | 7 +- 6 files changed, 96 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index cc76eb82..ca16d0a6 100644 --- a/.gitignore +++ b/.gitignore @@ -160,4 +160,4 @@ dmypy.json dummy/ tmp_model/ plots/ -demo/ \ No newline at end of file +demo/ diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index fe235c1d..ab29f5f2 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -31,39 +31,34 @@ def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: i predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] - tree, mapping = self._create_brute_tree(dataframe, criterion, n) + tree, cubes = self._create_brute_tree(criterion, n) predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( - row.to_dict(), tree, mapping + row.to_dict(), tree, cubes ) for _, row in dataframe[idx].iterrows()]) return predictions @staticmethod def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, - mapping: dict[int, GenericCube]) -> float | str: + cubes: list[GenericCube]) -> float | str: idx = tree.query([list(row.values())], k=1)[1][0][0] - return HyperCubePredictor._get_cube_output(mapping[idx], row) + return HyperCubePredictor._get_cube_output(cubes[idx], row) - def _create_brute_tree(self, dataframe: pd.DataFrame, - criterion: str = 'center', n: int = 2) -> (BallTree, dict[int, GenericCube]): + def _create_brute_tree(self, criterion: str = 'center', n: int = 2) -> (BallTree, list[GenericCube]): points = None if criterion == 'center': - points = [(cube.center(), i, cube) for i, cube in enumerate(self._hypercubes)] + points = [(cube.center(), cube) for cube in self._hypercubes] elif criterion == 'density': - points = [(cube.filter_dataframe(dataframe).describe().loc['mean'], i, cube) - for i, cube in enumerate(self._hypercubes)] - points = [(Point(df.index.values, df.values), i, cube) for df, i, cube in points] + points = [(cube.barycenter, cube) for cube in self._hypercubes] elif criterion == 'corner': points = [(corner, cube) for cube in self._hypercubes for corner in cube.corners()] - points = [(point[0], i, point[1]) for i, point in enumerate(points)] elif criterion == 'perimeter': points = [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] - points = [(point[0], i, point[1]) for i, point in enumerate(points)] else: raise NotImplementedError("'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density']") - return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True), leaf_size=2), \ - {point[1]: point[2] for point in points} + return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True)), \ + [point[1] for point in points] def _predict_from_cubes(self, data: dict[str, float]) -> float | str | None: for cube in self._hypercubes: diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index fff183bb..014f111b 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -39,8 +39,7 @@ def __to_cube(self, point: Point) -> GenericCube: return cube def __clean(self, data: pd.DataFrame) -> pd.DataFrame: - tree = BallTree(data.iloc[:, :-1], leaf_size=2) - _, idx = tree.query(data.iloc[:, :-1], k=self.k) + _, idx = BallTree(data.iloc[:, :-1]).query(data.iloc[:, :-1], k=self.k) # how many output classes are associated with the k neighbors count = np.array(list(map(lambda indices: len(data.iloc[indices].iloc[:, -1].unique()), idx))) # instances with neighbors of different classes are discarded @@ -52,8 +51,7 @@ def __sort_cubes(self): self._hypercubes = [cube[2] for cube in cubes] def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFrame): - tree = BallTree(data.iloc[:, :-1], leaf_size=2) - return DiViNE.__pop(data,self.vicinity_function(tree, cube)) + return DiViNE.__pop(data,self.vicinity_function(BallTree(data.iloc[:, :-1]), cube)) @staticmethod def __closest_to_center(tree: BallTree, cube: GenericCube): diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 777c5e61..e7b29479 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -70,6 +70,7 @@ def __init__(self, dimension: dict[str, tuple[float, float]] = None, limits: set self._limits = limits if limits is not None else set() self._output = output self._diversity = 0.0 + self._barycenter = Point([], []) def __contains__(self, point: dict[str, float]) -> bool: """ @@ -114,6 +115,10 @@ def output(self) -> float | str | LinearRegression: def diversity(self) -> float: return self._diversity + @property + def barycenter(self) -> Point: + return self._barycenter + def _fit_dimension(self, dimension: dict[str, tuple[float, float]]) -> dict[str, tuple[float, float]]: new_dimension: dict[str, tuple[float, float]] = {} for key, value in dimension.items(): @@ -283,9 +288,8 @@ def split(point: Point, feature: str, n: int): for primary in self._dimensions: new_points = [Point([], [])] for secondary in self._dimensions: - if primary != secondary: - new_points = np.array([duplicate(point, secondary) for point in new_points]).flatten() - new_points = np.array([split(point, primary, n) for point in new_points]).flatten() + new_points = np.array([duplicate(point, secondary) if primary != secondary else + split(point, primary, n) for point in new_points]).flatten() points = points + list(new_points) return points @@ -342,6 +346,8 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: predictions = predictor.predict(filtered) self._output = np.mean(predictions) self._diversity = np.std(predictions) + means = filtered.describe().loc['mean'] + self._barycenter = Point(means.index.values, means.values) # TODO: why this is not a property? def init_diversity(self, std: float) -> None: @@ -358,6 +364,8 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: predictions = predictor.predict(filtered) self._output.fit(filtered, predictions) self._diversity = (abs(self._output.predict(filtered) - predictions)).mean() + means = filtered.describe().loc['mean'] + self._barycenter = Point(means.index.values, means.values) def copy(self) -> RegressionCube: return RegressionCube(self.dimensions.copy()) @@ -384,6 +392,8 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: predictions = predictor.predict(filtered) self._output = mode(predictions) self._diversity = 1 - sum(prediction == self.output for prediction in predictions) / len(filtered) + means = filtered.describe().loc['mean'] + self._barycenter = Point(means.index.values, means.values) def copy(self) -> ClassificationCube: return ClassificationCube(self.dimensions.copy(), self._limits.copy(), self._output) diff --git a/psyke/utils/plot.py b/psyke/utils/plot.py index 2bcbca82..5db7a9e8 100644 --- a/psyke/utils/plot.py +++ b/psyke/utils/plot.py @@ -7,10 +7,75 @@ from matplotlib.lines import Line2D from tuprolog.solve.prolog import prolog_solver from tuprolog.theory import Theory, mutable_theory -from psyke.utils.logic import data_to_struct, pretty_theory, get_in_rule, get_not_in_rule + +from psyke.extraction.hypercubic import HyperCubeExtractor +from psyke.utils.logic import data_to_struct, get_in_rule, get_not_in_rule import matplotlib -matplotlib.use('TkAgg') +#matplotlib.use('TkAgg') + + +def plot_init(xlim, ylim, xlabel, ylabel, size=(4, 3), equal=False): + plt.figure(figsize=size) + if equal: + plt.gca().set_aspect(1) + plt.xlim(xlim) + plt.ylim(ylim) + plt.gca().set_xlabel(xlabel) + plt.gca().set_ylabel(ylabel) + plt.gca().set_rasterized(True) + + +def plot_point(x, y, color, marker): + plt.scatter(x, y, c=color, marker=marker) + + +def plot_classification_samples(dataframe, classes, colors, markers, labels, loc, name, show=True): + marks = [Line2D([0], [0], color=c, marker=m, lw="0") for c, m in zip(colors, markers)] + + for cl, c, m in zip(classes, colors, markers): + df = dataframe[dataframe.target == cl] + plot_point(df["petal length"], df["petal width"], c, m) + + plt.gca().legend(marks, labels, loc=loc) + plt.savefig("plot/{}.pdf".format(name), dpi=500, bbox_inches='tight') + if show: + plt.show() + + +def plot_boundaries(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + a: float = .5, h: str = '////////', ls='-', e=.05): + for cube in extractor._hypercubes: + plt.gca().fill_between((cube[x][0] - e, cube[x][1] + e), cube[y][0] - e, cube[y][1] + e, + fc='none', ec=colors[cube.output], alpha=a, hatch=h, linestyle=ls) + + +def plot_perimeters(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], n: int = 5, + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + for corner in cube.perimeter_samples(n): + plt.scatter(corner[x], corner[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) + + +def plot_centers(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + center = cube.center() + plt.scatter(center[x], center[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) + + +def plot_corners(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + for corner in cube.corners(): + plt.scatter(corner[x], corner[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) + + +def plot_barycenters(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + center = cube.barycenter + plt.scatter(center[x], center[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) def predict_from_theory(theory: Theory, data: pd.DataFrame) -> list[float or str]: diff --git a/requirements.txt b/requirements.txt index 16024bcd..e87c416d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,13 +2,14 @@ build==0.10.0 twine==4.0.2 numpy==1.23.5 pandas==2.0.2 -scikit-learn==1.2.2 +scikit-learn>=1.1.1 2ppy==0.4.0 skl2onnx==1.14.1 onnxruntime==1.14.1 -tensorflow==2.12.0 +tensorflow>=2.7.2 parameterized==0.9.0 protobuf==3.20.3 setuptools==67.8.0 kneed==0.8.3 -sympy==1.12 \ No newline at end of file +sympy==1.12 +matplotlib>=3.7.1 \ No newline at end of file From f1eb6c6300df8b078ef0aa717e167bf62b4078ca Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sat, 17 Jun 2023 13:16:09 +0200 Subject: [PATCH 05/67] feat: added COSMiK extractor --- demo/DemoRegression.ipynb | 185 +++++++++++++++++- psyke/__init__.py | 63 ++++-- psyke/extraction/hypercubic/__init__.py | 5 + .../extraction/hypercubic/cosmik/__init__.py | 43 ++++ .../extraction/hypercubic/divine/__init__.py | 15 +- psyke/tuning/orchid/__init__.py | 3 +- psyke/tuning/pedro/__init__.py | 6 +- 7 files changed, 285 insertions(+), 35 deletions(-) create mode 100644 psyke/extraction/hypercubic/cosmik/__init__.py diff --git a/demo/DemoRegression.ipynb b/demo/DemoRegression.ipynb index 68f73ceb..1aa736c0 100644 --- a/demo/DemoRegression.ipynb +++ b/demo/DemoRegression.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "dataset = pd.read_csv(\"../test/resources/datasets/df.csv\")\n", - "dataset = dataset[[\"X\", \"Y\", \"Z4\"]].dropna()\n", + "dataset = dataset[[\"X\", \"Y\", \"Z0\"]].dropna()\n", "#dataset = pd.read_csv(\"../test/resources/datasets/CCPP.csv\", sep=\";\", decimal=\",\")\n", "#dataset" ] @@ -69,7 +69,7 @@ "metadata": {}, "outputs": [], "source": [ - "train, test = train_test_split(dataset, test_size=0.5, random_state=10)" + "train, test = train_test_split(dataset, test_size=0.85, random_state=10)" ] }, { @@ -109,8 +109,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "MAE = 0.08\n", - "MSE = 0.03\n", + "MAE = 0.00\n", + "MSE = 0.00\n", "R2 = 1.00\n" ] } @@ -160,8 +160,8 @@ " f'{scores[EvaluableModel.RegressionScore.R2][1]:.2f} (BB)')\n", "\n", "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, EvaluableModel.Task.REGRESSION,\n", - " [EvaluableModel.RegressionScore.MAE, EvaluableModel.RegressionScore.MSE,\n", + " return extractor.score(test, predictor, True, True, task=EvaluableModel.Task.REGRESSION,\n", + " scoring_function=[EvaluableModel.RegressionScore.MAE, EvaluableModel.RegressionScore.MSE,\n", " EvaluableModel.RegressionScore.R2])" ] }, @@ -173,6 +173,179 @@ "We create several extractors that use ITER, GridEx and GridREx algorithms to extract prolog rules from the predictor." ] }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "COSMiK performance (3 rules with 90.03% coverage):\n", + "MAE = 0.11 (data), 0.11 (BB)\n", + "MSE = 0.05 (data), 0.05 (BB)\n", + "R2 = 0.92 (data), 0.92 (BB)\n", + "\n", + "COSMiK extracted rules:\n", + "\n", + "'Z0'(X, Y, 1.0) :-\n", + " X in [0.0, 0.48], Y in [0.0, 0.48].\n", + "'Z0'(X, Y, -0.98) :-\n", + " X in [0.65, 1.0], Y in [0.0, 0.46].\n", + "'Z0'(X, Y, -0.13) :-\n", + " X in [0.05, 0.99], Y in [0.59, 1.0].\n" + ] + } + ], + "source": [ + "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True)\n", + "theory_from_cosmik = cosmik.extract(train)\n", + "scores, completeness = get_scores(cosmik, test, predictor)\n", + "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "COSMiK performance (4 rules with 89.92% coverage):\n", + "MAE = 0.08 (data), 0.08 (BB)\n", + "MSE = 0.03 (data), 0.03 (BB)\n", + "R2 = 0.96 (data), 0.96 (BB)\n", + "\n", + "COSMiK extracted rules:\n", + "\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.0, 0.48], Y in [0.0, 0.48], Z0 is 1.0.\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.65, 1.0], Y in [0.0, 0.44], Z0 is -1.0.\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.94, 1.0], Y in [0.79, 1.0], Z0 is -1.0.\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.05, 0.98], Y in [0.57, 0.99], Z0 is 0.53 - 0.82 * X - 0.28 * Y.\n" + ] + } + ], + "source": [ + "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True, output=Target.REGRESSION)\n", + "theory_from_cosmik = cosmik.extract(train)\n", + "scores, completeness = get_scores(cosmik, test, predictor)\n", + "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", + "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", + "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", + "\n", + "**********************\n", + "Best Algorithm.GRIDEX\n", + "**********************\n", + "MAE = 2.75, 5 rules\n", + "Threshold = 0.00\n", + "Iterations = 1\n", + "Strategy = Adaptive ([(0.99, 5)])\n", + "\n", + "**********************\n", + "Best Predictive loss\n", + "**********************\n", + "MAE = 2.23, 9 rules\n", + "Threshold = 0.00\n", + "Iterations = 1\n", + "Strategy = Fixed (3)\n", + "\n", + "**********************\n", + "Best N rules\n", + "**********************\n", + "MAE = 4.06, 2 rules\n", + "Threshold = 0.00\n", + "Iterations = 1\n", + "Strategy = Adaptive ([(0.99, 2)])\n", + "\n", + "GridEx performance (5 rules with 100.00% coverage):\n", + "MAE = 2.79 (data), 2.79 (BB)\n", + "MSE = 14.54 (data), 14.52 (BB)\n", + "R2 = 0.42 (data), 0.42 (BB)\n", + "\n", + "GridEx extracted rules:\n", + "\n", + "'Z4'(X, Y, 3.99) :-\n", + " Y in [-0.00, 0.19].\n", + "'Z4'(X, Y, 6.53) :-\n", + " Y in [0.19, 0.4].\n", + "'Z4'(X, Y, 9.37) :-\n", + " Y in [0.4, 0.6].\n", + "'Z4'(X, Y, 11.42) :-\n", + " Y in [0.6, 0.80].\n", + "'Z4'(X, Y, 2.75) :-\n", + " Y in [0.80, 1.00].\n" + ] + } + ], + "source": [ + "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", + " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL)\n", + "pedro.search()\n", + "(_, _, threshold, grid) = pedro.get_best()[0]\n", + "\n", + "gridEx = Extractor.gridex(predictor, grid, threshold=threshold)\n", + "theory_from_gridEx = gridEx.extract(train)\n", + "scores, completeness = get_scores(gridEx, test, predictor)\n", + "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": 8, diff --git a/psyke/__init__.py b/psyke/__init__.py index 3e7ae087..5821d368 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -160,59 +160,81 @@ def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: """ raise NotImplementedError('extract') - def mae(self, dataframe: pd.DataFrame, predictor=None) -> float: + def mae(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' MAE w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the mean absolute error. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the mean absolute error (MAE) of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.REGRESSION, - [Extractor.RegressionScore.MAE])[Extractor.RegressionScore.MAE][-1] + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.REGRESSION, [Extractor.RegressionScore.MAE])[Extractor.RegressionScore.MAE][-1] - def mse(self, dataframe: pd.DataFrame, predictor=None) -> float: + def mse(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' MSE w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the mean squared error. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the mean squared error (MSE) of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.REGRESSION, - [Extractor.RegressionScore.MSE])[Extractor.RegressionScore.MSE][-1] + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.REGRESSION, [Extractor.RegressionScore.MSE])[Extractor.RegressionScore.MSE][-1] - def r2(self, dataframe: pd.DataFrame, predictor=None) -> float: + def r2(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' R2 score w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the R2 score. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the R2 score of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, Extractor.Task.REGRESSION, [Extractor.RegressionScore.R2])[Extractor.RegressionScore.R2][-1] - def accuracy(self, dataframe: pd.DataFrame, predictor=None) -> float: + def accuracy(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' accuracy classification score w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the accuracy classification score. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the accuracy classification score of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.CLASSIFICATION, + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.CLASSIFICATION, [Extractor.ClassificationScore.ACCURACY])[Extractor.ClassificationScore.ACCURACY][-1] - def f1(self, dataframe: pd.DataFrame, predictor=None) -> float: + def f1(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' F1 score w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the F1 score. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the F1 score of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.CLASSIFICATION, + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.CLASSIFICATION, [Extractor.ClassificationScore.F1])[Extractor.ClassificationScore.F1][-1] @staticmethod @@ -227,7 +249,7 @@ def cart(predictor, max_depth: int = 3, max_leaves: int = 3, @staticmethod def divine(predictor, k: int = 5, patience: int = 15, close_to_center: bool = True, - discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: + discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: """ Creates a new DiViNE extractor. """ @@ -235,6 +257,17 @@ def divine(predictor, k: int = 5, patience: int = 15, close_to_center: bool = Tr return DiViNE(predictor, k=k, patience=patience, close_to_center=close_to_center, discretization=discretization, normalization=normalization) + @staticmethod + def cosmik(predictor, max_components: int = 4, k: int = 5, patience: int = 15, close_to_center: bool = True, + output: Target = Target.CONSTANT, + discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: + """ + Creates a new COSMiK extractor. + """ + from psyke.extraction.hypercubic.cosmik import COSMiK + return COSMiK(predictor, max_components=max_components, k=k, patience=patience, close_to_center=close_to_center, + output=output, discretization=discretization, normalization=normalization) + @staticmethod def iter(predictor, min_update: float = 0.1, n_points: int = 1, max_iterations: int = 600, min_examples: int = 250, threshold: float = 0.1, fill_gaps: bool = True, normalization: dict[str, tuple[float, float]] = None, @@ -267,8 +300,8 @@ def gridrex(predictor, grid, min_examples: int = 250, threshold: float = 0.1, return GridREx(predictor, grid, min_examples, threshold, normalization, seed) @staticmethod - def creepy(predictor, clustering, depth: int, error_threshold: float, output, gauss_components: int = 2, - ranks: [(str, float)] = [], ignore_threshold: float = 0.0, + def creepy(predictor, clustering, depth: int, error_threshold: float, output: Target = Target.CONSTANT, + gauss_components: int = 2, ranks: [(str, float)] = [], ignore_threshold: float = 0.0, normalization: dict[str, tuple[float, float]] = None) -> Extractor: """ Creates a new CReEPy extractor. diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index ab29f5f2..0bb73946 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -95,6 +95,11 @@ def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: return RegressionCube() return ClassificationCube() + def _sort_cubes(self): + cubes = [(cube.diversity, i, cube) for i, cube in enumerate(self._hypercubes)] + cubes.sort() + self._hypercubes = [cube[2] for cube in cubes] + @staticmethod def _create_head(dataframe: pd.DataFrame, variables: list[Var], output: float | LinearRegression) -> Struct: return create_head(dataframe.columns[-1], variables[:-1], output) \ diff --git a/psyke/extraction/hypercubic/cosmik/__init__.py b/psyke/extraction/hypercubic/cosmik/__init__.py new file mode 100644 index 00000000..7412a96d --- /dev/null +++ b/psyke/extraction/hypercubic/cosmik/__init__.py @@ -0,0 +1,43 @@ +import pandas as pd +from sklearn.mixture import GaussianMixture +from tuprolog.theory import Theory + +from psyke import Target, Extractor +from psyke.clustering.utils import select_gaussian_mixture +from psyke.extraction.hypercubic import HyperCube, HyperCubeExtractor, RegressionCube + + +class COSMiK(HyperCubeExtractor): + """ + Explanator implementing COSMiK algorithm. + """ + + def __init__(self, predictor, max_components: int = 4, k: int = 5, patience: int = 15, close_to_center: bool = True, + output: Target = Target.CONSTANT, discretization=None, normalization=None): + super().__init__(predictor, Target.REGRESSION, discretization, normalization) + self.max = max_components + self.k = k + self.patience = patience + self.output = output + self.close_to_center = close_to_center + + def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: + X, y = dataframe.iloc[:, :-1], dataframe.iloc[:, -1] + + _, n, _ = select_gaussian_mixture(dataframe, self.max) + gmm = GaussianMixture(n) + gmm.fit(X, y) + + divine = Extractor.divine(gmm, self.k, self.patience, self.close_to_center, + self.discretization, self.normalization) + df = X.join(pd.DataFrame(gmm.predict(X))) + df.columns = dataframe.columns + divine.extract(df) + + self._hypercubes = [HyperCube(cube.dimensions) if self.output == Target.CONSTANT else + RegressionCube(cube.dimensions) for cube in divine._hypercubes] + for cube in self._hypercubes: + cube.update(dataframe, self.predictor) + + self._sort_cubes() + return self._create_theory(dataframe, sort) \ No newline at end of file diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index 014f111b..d5674776 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -19,7 +19,7 @@ def __init__(self, predictor, k: int = 5, patience: int = 15, close_to_center: b super().__init__(predictor, Target.CLASSIFICATION, discretization, normalization) self.k = k self.patience = patience - self.vicinity_function = DiViNE.__closest_to_center if close_to_center else DiViNE.__closest_to_corners + self.vicinity_function = DiViNE.closest_to_center if close_to_center else DiViNE.closest_to_corners @staticmethod def __pop(data: pd.DataFrame, idx: int = None) -> (Point, pd.DataFrame): @@ -45,20 +45,15 @@ def __clean(self, data: pd.DataFrame) -> pd.DataFrame: # instances with neighbors of different classes are discarded return data[count == 1] - def __sort_cubes(self): - cubes = [(cube.diversity, i, cube) for i, cube in enumerate(self._hypercubes)] - cubes.sort() - self._hypercubes = [cube[2] for cube in cubes] - def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFrame): - return DiViNE.__pop(data,self.vicinity_function(BallTree(data.iloc[:, :-1]), cube)) + return DiViNE.__pop(data, self.vicinity_function(BallTree(data.iloc[:, :-1]), cube)) @staticmethod - def __closest_to_center(tree: BallTree, cube: GenericCube): + def closest_to_center(tree: BallTree, cube: GenericCube): return tree.query([list(cube.center().dimensions.values())], k=1)[1][0][-1] @staticmethod - def __closest_to_corners(tree: BallTree, cube: GenericCube): + def closest_to_corners(tree: BallTree, cube: GenericCube): distance, idx = tree.query([list(point.dimensions.values()) for point in cube.corners()], k=1) return idx[np.argmin(distance)][-1] @@ -84,5 +79,5 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort self._hypercubes.append(cube) if len(discarded) > 0: data = pd.concat([data] + [d.to_dataframe() for d in discarded]).reset_index(drop=True) - self.__sort_cubes() + self._sort_cubes() return self._create_theory(dataframe, sort) diff --git a/psyke/tuning/orchid/__init__.py b/psyke/tuning/orchid/__init__.py index d1dfe6c8..cf76eefa 100644 --- a/psyke/tuning/orchid/__init__.py +++ b/psyke/tuning/orchid/__init__.py @@ -57,7 +57,8 @@ def __search_threshold(self, depth): (EvaluableModel.Task.CLASSIFICATION, EvaluableModel.ClassificationScore.INVERSE_ACCURACY) \ if self.output == Target.CLASSIFICATION else \ (EvaluableModel.Task.REGRESSION, EvaluableModel.RegressionScore.MAE) - p, n = clustering.score(self.dataframe, None, False, False, task, [metric])[metric][0], clustering.n_rules + p, n = clustering.score(self.dataframe, None, False, False, task=task, + scoring_function=[metric])[metric][0], clustering.n_rules print(f"Predictive loss = {p:.2f}, {n} rules") diff --git a/psyke/tuning/pedro/__init__.py b/psyke/tuning/pedro/__init__.py index feb57e5e..9e6aae3b 100644 --- a/psyke/tuning/pedro/__init__.py +++ b/psyke/tuning/pedro/__init__.py @@ -4,10 +4,10 @@ from psyke import Extractor from psyke.extraction.hypercubic import Grid, FeatureRanker from psyke.extraction.hypercubic.strategy import AdaptiveStrategy, FixedStrategy -from psyke.tuning import Objective, Optimizer +from psyke.tuning import Objective, GridOptimizer -class PEDRO(Optimizer): +class PEDRO(GridOptimizer): class Algorithm(Enum): GRIDEX = 1, GRIDREX = 2 @@ -71,7 +71,7 @@ def __search_depth(self, strategy, critical, max_partitions): for iterations in range(self.max_depth): grid = Grid(iterations + 1, strategy) p = self.__search_threshold(grid, critical, max_partitions) - b = Optimizer._best(p)[1] + b = GridOptimizer._best(p)[1] print() improvement = self._depth_improvement( [best[0], best[1]], [b[0], b[1]] From 965f5bd83e0079c8c222269ad5b14d621182641c Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:22:25 +0200 Subject: [PATCH 06/67] fix: normalization --- demo/DemoClassification.ipynb | 37 +++++++++++++++---------- demo/DemoRegressionScaled.ipynb | 36 +++++++++--------------- psyke/extraction/hypercubic/__init__.py | 9 ++++-- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 80848efe..3bd95396 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -265,20 +265,29 @@ "text": [ "DiViNE performance (3 rules with 73.68% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "\n", - "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "DiViNE extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" + "F1 = 0.96 (data), 1.00 (BB)\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/1973727682.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 7\u001B[1;33m \u001B[0mscores\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcompleteness\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdivine\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'density'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 8\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34mf'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 9\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/301734117.py\u001B[0m in \u001B[0;36mget_scores\u001B[1;34m(extractor, test, predictor, brute, criterion, n)\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mextractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbrute\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mFalse\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;34m'density'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 8\u001B[1;33m return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n\u001B[0m\u001B[0;32m 9\u001B[0m [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mscore\u001B[1;34m(self, dataframe, predictor, fidelity, completeness, brute, criterion, n, task, scoring_function)\u001B[0m\n\u001B[0;32m 85\u001B[0m extracted = np.array(\n\u001B[0;32m 86\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[0mbrute\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 87\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 88\u001B[0m )\n\u001B[0;32m 89\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mextracted\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " ] } ], diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index b57fab34..98331ff5 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -170,32 +170,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "ITER performance (9 rules):\n", - "MAE = 0.04\n", - "MAE fidelity = 0.04\n", - "R2 = 0.88\n", - "R2 fidelity = 0.88\n", + "ITER performance (4 rules):\n", + "MAE = 0.55\n", + "MAE fidelity = 0.55\n", + "R2 = -6.34\n", + "R2 fidelity = -6.34\n", "\n", "ITER extracted rules:\n", "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.46], Y in [0.00, 0.43].\n", - "z(X, Y, 0.59) :-\n", - " X in [0.00, 0.46], Y in [0.43, 0.53].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.46], Y in [0.53, 0.99].\n", - "z(X, Y, 0.47) :-\n", - " X in [0.46, 0.56], Y in [0.00, 0.43].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.56, 0.99], Y in [0.00, 0.43].\n", - "z(X, Y, 0.18) :-\n", - " X in [0.46, 0.56], Y in [0.53, 0.99].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.56, 0.99], Y in [0.53, 0.99].\n", - "z(X, Y, 0.32) :-\n", - " X in [0.46, 0.56], Y in [0.43, 0.53].\n", - "z(X, Y, 0.20) :-\n", - " X in [0.56, 0.99], Y in [0.43, 0.53].\n" + "z(X, Y, 1.11) :-\n", + " X in [-1.70, -0.00], Y in [-1.69, 0.22].\n", + "z(X, Y, 0.19) :-\n", + " X in [-1.70, -0.00], Y in [0.22, 1.75].\n", + "z(X, Y, -0.11) :-\n", + " X in [-0.00, 1.75], Y in [-1.69, 0.22].\n", + "z(X, Y, -1.29) :-\n", + " X in [-0.00, 1.75], Y in [0.22, 1.75].\n" ] } ], diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 0bb73946..e0fc7e95 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -25,7 +25,11 @@ def __init__(self, output=Target.CONSTANT, normalization=None): self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: - return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) + predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) + m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] + idx = [prediction is not None for prediction in predictions] + predictions[idx] = predictions[idx] * s + m + return predictions def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) @@ -36,7 +40,8 @@ def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: i predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( row.to_dict(), tree, cubes ) for _, row in dataframe[idx].iterrows()]) - return predictions + m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] + return np.array(predictions) * s + m @staticmethod def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, From f2013a30713f63b7daa6de0b293740801e6a16e0 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:34:45 +0200 Subject: [PATCH 07/67] fix(HyperCubeExtractor): remove cubes with count <= 1 --- psyke/__init__.py | 4 ++-- psyke/extraction/hypercubic/__init__.py | 4 ++++ psyke/extraction/hypercubic/cosmik/__init__.py | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index 5821d368..699eacaa 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -302,13 +302,13 @@ def gridrex(predictor, grid, min_examples: int = 250, threshold: float = 0.1, @staticmethod def creepy(predictor, clustering, depth: int, error_threshold: float, output: Target = Target.CONSTANT, gauss_components: int = 2, ranks: [(str, float)] = [], ignore_threshold: float = 0.0, - normalization: dict[str, tuple[float, float]] = None) -> Extractor: + discretization=None, normalization: dict[str, tuple[float, float]] = None) -> Extractor: """ Creates a new CReEPy extractor. """ from psyke.extraction.hypercubic.creepy import CReEPy return CReEPy(predictor, depth, error_threshold, output, gauss_components, ranks, ignore_threshold, - normalization, clustering) + discretization, normalization, clustering) @staticmethod def real(predictor, discretization=None) -> Extractor: diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index e0fc7e95..db3b9d1a 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -114,7 +114,11 @@ def _create_head(dataframe: pd.DataFrame, variables: list[Var], output: float | def _ignore_dimensions(self) -> Iterable[str]: return [] + def __drop(self, dataframe: pd.DataFrame): + self._hypercubes = [cube for cube in self._hypercubes if cube.count(dataframe) > 1] + def _create_theory(self, dataframe: pd.DataFrame, sort: bool = True) -> Theory: + self.__drop() new_theory = mutable_theory() for cube in self._hypercubes: logger.info(cube.output) diff --git a/psyke/extraction/hypercubic/cosmik/__init__.py b/psyke/extraction/hypercubic/cosmik/__init__.py index 7412a96d..c7be3a89 100644 --- a/psyke/extraction/hypercubic/cosmik/__init__.py +++ b/psyke/extraction/hypercubic/cosmik/__init__.py @@ -34,8 +34,8 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort df.columns = dataframe.columns divine.extract(df) - self._hypercubes = [HyperCube(cube.dimensions) if self.output == Target.CONSTANT else - RegressionCube(cube.dimensions) for cube in divine._hypercubes] + self._hypercubes = [HyperCube(cube.dimensions.copy()) if self.output == Target.CONSTANT else + RegressionCube(cube.dimensions.copy()) for cube in divine._hypercubes] for cube in self._hypercubes: cube.update(dataframe, self.predictor) From fa38a0b5aa5c517a81b0c1aa22acef6c766ec377 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:01:51 +0200 Subject: [PATCH 08/67] fix(HyperCubeExtractor): fix cube removal --- psyke/extraction/hypercubic/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index db3b9d1a..ee2e293f 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -27,9 +27,7 @@ def __init__(self, output=Target.CONSTANT, normalization=None): def _predict(self, dataframe: pd.DataFrame) -> Iterable: predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] - idx = [prediction is not None for prediction in predictions] - predictions[idx] = predictions[idx] * s + m - return predictions + return np.array([None if prediction is None else prediction * s + m for prediction in predictions]) def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) @@ -118,7 +116,7 @@ def __drop(self, dataframe: pd.DataFrame): self._hypercubes = [cube for cube in self._hypercubes if cube.count(dataframe) > 1] def _create_theory(self, dataframe: pd.DataFrame, sort: bool = True) -> Theory: - self.__drop() + self.__drop(dataframe) new_theory = mutable_theory() for cube in self._hypercubes: logger.info(cube.output) From 1b5c5e1deda0224124dbe0984fda152fa03d95b2 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:26:35 +0200 Subject: [PATCH 09/67] fix(HyperCubePredictor): scaled predictions --- psyke/extraction/hypercubic/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index ee2e293f..7e2abf8b 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -26,20 +26,20 @@ def __init__(self, output=Target.CONSTANT, normalization=None): def _predict(self, dataframe: pd.DataFrame) -> Iterable: predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] + m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] return np.array([None if prediction is None else prediction * s + m for prediction in predictions]) def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] - - tree, cubes = self._create_brute_tree(criterion, n) - - predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( - row.to_dict(), tree, cubes - ) for _, row in dataframe[idx].iterrows()]) - m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] - return np.array(predictions) * s + m + if sum(idx) > 0: + tree, cubes = self._create_brute_tree(criterion, n) + m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] + predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( + row.to_dict(), tree, cubes + ) * s + m for _, row in dataframe[idx].iterrows()]) + + return np.array(predictions) @staticmethod def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, From 4752e310078d66979fce61b555cae5f9fb3d1995 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:32:23 +0200 Subject: [PATCH 10/67] fix: scaled predictions --- psyke/__init__.py | 17 +++++++++++++---- psyke/extraction/cart/__init__.py | 4 ++-- psyke/extraction/hypercubic/__init__.py | 10 +++------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index 699eacaa..9ed1e4ae 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -56,16 +56,25 @@ def predict(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None) -> It :param mapping: for one-hot encoding. :return: a list of predictions. """ - ys = self._predict(dataframe) + return self.__convert(self._predict(dataframe), mapping) + + def _predict(self, dataframe: pd.DataFrame) -> Iterable: + raise NotImplementedError('predict') + + def __convert(self, ys: Iterable, mapping: dict[str: int] = None) -> Iterable: if mapping is not None: inverse_mapping = {v: k for k, v in mapping.items()} ys = [inverse_mapping[y] for y in ys] + if self.normalization is not None: + m, s = self.normalization[list(self.normalization.keys())[-1]] + ys = [prediction if prediction is None else ys * s + m for prediction in ys] return ys - def _predict(self, dataframe: pd.DataFrame, criterion: str = 'perimeter') -> Iterable: - raise NotImplementedError('predict') + def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, + mapping: dict[str: int] = None) -> Iterable: + return self.__convert(self._brute_predict(dataframe), mapping) - def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: raise NotImplementedError('brute_predict') def unscale(self, values, name): diff --git a/psyke/extraction/cart/__init__.py b/psyke/extraction/cart/__init__.py index c2ceabff..444045ed 100644 --- a/psyke/extraction/cart/__init__.py +++ b/psyke/extraction/cart/__init__.py @@ -77,8 +77,8 @@ def _extract(self, data: pd.DataFrame, mapping: dict[str: int] = None, sort: boo self._cart_predictor.predictor.fit(data.iloc[:, :-1], data.iloc[:, -1]) return self._create_theory(data, mapping, sort) - def _predict(self, data) -> Iterable: - return self._cart_predictor.predict(data) + def _predict(self, dataframe: pd.DataFrame) -> Iterable: + return self._cart_predictor.predict(dataframe) @property def n_rules(self) -> int: diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 7e2abf8b..0db5a8d4 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -25,20 +25,16 @@ def __init__(self, output=Target.CONSTANT, normalization=None): self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: - predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] - return np.array([None if prediction is None else prediction * s + m for prediction in predictions]) + return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] if sum(idx) > 0: tree, cubes = self._create_brute_tree(criterion, n) - m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( row.to_dict(), tree, cubes - ) * s + m for _, row in dataframe[idx].iterrows()]) - + ) for _, row in dataframe[idx].iterrows()]) return np.array(predictions) @staticmethod From 1137a8ab512011718c5d6edbffb1fc296e29b641 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:42:07 +0200 Subject: [PATCH 11/67] fix: brute prediction --- psyke/__init__.py | 5 +++-- psyke/extraction/hypercubic/__init__.py | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index 9ed1e4ae..c9533ff4 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -72,9 +72,10 @@ def __convert(self, ys: Iterable, mapping: dict[str: int] = None) -> Iterable: def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, mapping: dict[str: int] = None) -> Iterable: - return self.__convert(self._brute_predict(dataframe), mapping) + return self.__convert(self._brute_predict(dataframe, criterion, n, mapping), mapping) - def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, + mapping: dict[str: int] = None) -> Iterable: raise NotImplementedError('brute_predict') def unscale(self, values, name): diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 0db5a8d4..9f9a978e 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -9,7 +9,7 @@ from sklearn.linear_model import LinearRegression from tuprolog.core import Var, Struct, clause from tuprolog.theory import Theory, mutable_theory -from psyke import logger, PedagogicalExtractor +from psyke import logger, PedagogicalExtractor, EvaluableModel from psyke.extraction.hypercubic.hypercube import HyperCube, RegressionCube, ClassificationCube, ClosedCube, Point, \ GenericCube from psyke.utils.logic import create_variable_list, create_head, to_var, Simplifier @@ -18,16 +18,17 @@ from sklearn.neighbors import BallTree -class HyperCubePredictor: +class HyperCubePredictor(EvaluableModel): def __init__(self, output=Target.CONSTANT, normalization=None): + super().__init__(normalization) self._hypercubes = [] self._output = output - self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, + mapping: dict[str: int] = None) -> Iterable: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] if sum(idx) > 0: From 1d57906be79b755721b9b3a1abf308999818b715 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:59:55 +0200 Subject: [PATCH 12/67] fix: class hierarchy --- demo/DemoRegression.ipynb | 18 ++++++++++-------- psyke/extraction/hypercubic/__init__.py | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/demo/DemoRegression.ipynb b/demo/DemoRegression.ipynb index 1aa736c0..3bef7b2a 100644 --- a/demo/DemoRegression.ipynb +++ b/demo/DemoRegression.ipynb @@ -181,19 +181,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "COSMiK performance (3 rules with 90.03% coverage):\n", - "MAE = 0.11 (data), 0.11 (BB)\n", - "MSE = 0.05 (data), 0.05 (BB)\n", - "R2 = 0.92 (data), 0.92 (BB)\n", + "COSMiK performance (4 rules with 87.83% coverage):\n", + "MAE = 0.00 (data), 0.00 (BB)\n", + "MSE = 0.00 (data), 0.00 (BB)\n", + "R2 = 1.00 (data), 1.00 (BB)\n", "\n", "COSMiK extracted rules:\n", "\n", "'Z0'(X, Y, 1.0) :-\n", " X in [0.0, 0.48], Y in [0.0, 0.48].\n", - "'Z0'(X, Y, -0.98) :-\n", - " X in [0.65, 1.0], Y in [0.0, 0.46].\n", - "'Z0'(X, Y, -0.13) :-\n", - " X in [0.05, 0.99], Y in [0.59, 1.0].\n" + "'Z0'(X, Y, -1.0) :-\n", + " X in [0.65, 1.0], Y in [0.0, 0.44].\n", + "'Z0'(X, Y, -1.0) :-\n", + " X in [0.90, 1.0], Y in [0.75, 1.0].\n", + "'Z0'(X, Y, 0.0) :-\n", + " X in [0.05, 0.83], Y in [0.57, 0.99].\n" ] } ], diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 9f9a978e..76edcd4a 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -85,8 +85,8 @@ def _get_cube_output(cube, data: dict[str, float]) -> float: class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC): def __init__(self, predictor, output, discretization=None, normalization=None): - PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) HyperCubePredictor.__init__(self, output=output, normalization=normalization) + PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: if self._output == Target.CONSTANT: From b2b9e9ca69a85c272814fd4cd5f441dffeff9023 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:30:17 +0200 Subject: [PATCH 13/67] fix: value conversion --- psyke/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index c9533ff4..dd8c571a 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -67,7 +67,7 @@ def __convert(self, ys: Iterable, mapping: dict[str: int] = None) -> Iterable: ys = [inverse_mapping[y] for y in ys] if self.normalization is not None: m, s = self.normalization[list(self.normalization.keys())[-1]] - ys = [prediction if prediction is None else ys * s + m for prediction in ys] + ys = [prediction if prediction is None else prediction * s + m for prediction in ys] return ys def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, From e307e442ecfac7cec3a3097716b0b0e329c5f1c2 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:37:53 +0200 Subject: [PATCH 14/67] fix(GridEx): normalization --- psyke/extraction/hypercubic/gridex/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyke/extraction/hypercubic/gridex/__init__.py b/psyke/extraction/hypercubic/gridex/__init__.py index 09272ee0..664554b6 100644 --- a/psyke/extraction/hypercubic/gridex/__init__.py +++ b/psyke/extraction/hypercubic/gridex/__init__.py @@ -17,7 +17,7 @@ class GridEx(HyperCubeExtractor): def __init__(self, predictor, grid: Grid, min_examples: int, threshold: float, normalization=None, seed=get_default_random_seed()): - super().__init__(predictor, Target.CONSTANT, normalization) + super().__init__(predictor, Target.CONSTANT, normalization=normalization) self.grid = grid self.min_examples = min_examples self.threshold = threshold From 75ca78d72710254427d89897aa0f970bbd3119b6 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 01:24:09 +0200 Subject: [PATCH 15/67] feat(brute prediction): added default mode --- psyke/__init__.py | 4 ++++ psyke/extraction/hypercubic/__init__.py | 20 +++++++++++++++----- psyke/utils/plot.py | 3 ++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index dd8c571a..d1f0fc0d 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -374,6 +374,10 @@ def __init__(self, predictor, discretization=None, normalization=None): Extractor.__init__(self, predictor=predictor, discretization=discretization, normalization=normalization) def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: + from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube + if isinstance(self, HyperCubeExtractor): + self._surrounding = HyperCube.create_surrounding_cube(dataframe, output=self._output) + self._surrounding.update(dataframe, self.predictor) new_y = self.predictor.predict(dataframe.iloc[:, :-1]) if mapping is not None: if hasattr(new_y[0], 'shape'): diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 76edcd4a..5becc59f 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -32,10 +32,17 @@ def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] if sum(idx) > 0: - tree, cubes = self._create_brute_tree(criterion, n) - predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( - row.to_dict(), tree, cubes - ) for _, row in dataframe[idx].iterrows()]) + if criterion == 'default': + if not isinstance(self, HyperCubeExtractor): + raise ValueError("'default' criterion only available for instances of HyperCubeExtractor") + predictions[idx] = np.array([HyperCubePredictor._get_cube_output( + self._surrounding, row + ) for _, row in dataframe[idx].iterrows()]) + else: + tree, cubes = self._create_brute_tree(criterion, n) + predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( + row.to_dict(), tree, cubes + ) for _, row in dataframe[idx].iterrows()]) return np.array(predictions) @staticmethod @@ -55,7 +62,9 @@ def _create_brute_tree(self, criterion: str = 'center', n: int = 2) -> (BallTree elif criterion == 'perimeter': points = [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] else: - raise NotImplementedError("'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density']") + raise NotImplementedError( + "'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density', 'default']" + ) return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True)), \ [point[1] for point in points] @@ -87,6 +96,7 @@ class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC): def __init__(self, predictor, output, discretization=None, normalization=None): HyperCubePredictor.__init__(self, output=output, normalization=normalization) PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) + self._surrounding = None def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: if self._output == Target.CONSTANT: diff --git a/psyke/utils/plot.py b/psyke/utils/plot.py index 5db7a9e8..04be6b96 100644 --- a/psyke/utils/plot.py +++ b/psyke/utils/plot.py @@ -160,6 +160,7 @@ def color_fader(v: float = 0., c1: str = 'green', c2: str = 'red'): pass # ax.text2D(0., 0.88, pretty_theory(theory, new_line=False), transform=ax.transAxes, fontsize=8) if isinstance(ys[0], str): - custom_lines = [Line2D([0], [0], marker='o', markerfacecolor=get_color(c), markersize=20, color='w') for c in classes] + custom_lines = [Line2D([0], [0], marker='o', markerfacecolor=get_color(c), + markersize=20, color='w') for c in classes] ax.legend(custom_lines, classes, loc='upper left', numpoints=1, ncol=3, fontsize=18, bbox_to_anchor=(0, 0)) plt.savefig(output, format='pdf') From 1a6abb9eacc00c47f06116344ef4886344cad7d0 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 01:52:25 +0200 Subject: [PATCH 16/67] fix(GridEx) --- demo/DemoClassification.ipynb | 51 +++++++++++++++-------------------- psyke/__init__.py | 9 ++++--- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 3bd95396..0c802f4f 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -265,29 +265,20 @@ "text": [ "DiViNE performance (3 rules with 73.68% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n" - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/1973727682.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 7\u001B[1;33m \u001B[0mscores\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcompleteness\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdivine\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'density'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 8\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34mf'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 9\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/301734117.py\u001B[0m in \u001B[0;36mget_scores\u001B[1;34m(extractor, test, predictor, brute, criterion, n)\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mextractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbrute\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mFalse\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;34m'density'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 8\u001B[1;33m return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n\u001B[0m\u001B[0;32m 9\u001B[0m [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mscore\u001B[1;34m(self, dataframe, predictor, fidelity, completeness, brute, criterion, n, task, scoring_function)\u001B[0m\n\u001B[0;32m 85\u001B[0m extracted = np.array(\n\u001B[0;32m 86\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[0mbrute\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 87\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 88\u001B[0m )\n\u001B[0;32m 89\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mextracted\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + "F1 = 0.96 (data), 1.00 (BB)\n", + "\n", + "DiViNE brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.95 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", + "\n", + "DiViNE extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" ] } ], @@ -313,24 +304,24 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 11, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "DiViNE performance (3 rules with 71.05% coverage):\n", + "DiViNE performance (3 rules with 73.68% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", "F1 = 0.96 (data), 1.00 (BB)\n", "\n", "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", + "Classification accuracy = 0.95 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", "\n", "DiViNE extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.7], PetalWidth in [1.0, 1.5].\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", @@ -360,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 12, "outputs": [ { "name": "stdout", @@ -417,7 +408,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 13, "outputs": [ { "name": "stdout", diff --git a/psyke/__init__.py b/psyke/__init__.py index d1f0fc0d..77bb12e8 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -375,9 +375,6 @@ def __init__(self, predictor, discretization=None, normalization=None): def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube - if isinstance(self, HyperCubeExtractor): - self._surrounding = HyperCube.create_surrounding_cube(dataframe, output=self._output) - self._surrounding.update(dataframe, self.predictor) new_y = self.predictor.predict(dataframe.iloc[:, :-1]) if mapping is not None: if hasattr(new_y[0], 'shape'): @@ -390,7 +387,11 @@ def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: new_y = pd.DataFrame(new_y).set_index(dataframe.index) data = dataframe.iloc[:, :-1].copy().join(new_y) data.columns = dataframe.columns - return self._extract(data, mapping, sort) + theory = self._extract(data, mapping, sort) + if isinstance(self, HyperCubeExtractor): + self._surrounding = HyperCube.create_surrounding_cube(dataframe, output=self._output) + self._surrounding.update(dataframe, self.predictor) + return theory def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: raise NotImplementedError('extract') From c738c97c84b70ccdf7764a464ea0560b7ebdd73b Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 2 Jul 2023 16:13:03 +0200 Subject: [PATCH 17/67] feat: removed duplicate points for perimeter-based vicinity --- psyke/extraction/hypercubic/hypercube.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index e7b29479..404430c5 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -276,6 +276,13 @@ def duplicate(point: Point, feature: str) -> Iterable[Point]: new_point_b[feature] = self.get_second(feature) return [new_point_a, new_point_b] + def remove_duplicates(points: Iterable[Point]) -> Iterable[Point]: + new_points = [] + for point in points: + if point not in new_points: + new_points.append(point) + return new_points + def split(point: Point, feature: str, n: int): points = [] for value in np.linspace(self.get_first(feature), self.get_second(feature), n): @@ -291,7 +298,7 @@ def split(point: Point, feature: str, n: int): new_points = np.array([duplicate(point, secondary) if primary != secondary else split(point, primary, n) for point in new_points]).flatten() points = points + list(new_points) - return points + return remove_duplicates(points) def is_adjacent(self, cube: HyperCube) -> str | None: adjacent = None From 1a0a80b53962917168a71b30d3397b64814f14d2 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:11:39 +0200 Subject: [PATCH 18/67] wip --- demo/DemoClassification.ipynb | 691 --------------------- demo/DemoClassificationDisc.ipynb | 452 -------------- demo/DemoRegressionScaled.ipynb | 990 +++++------------------------- demo/README.md | 40 -- demo/demo.py | 27 - 5 files changed, 138 insertions(+), 2062 deletions(-) delete mode 100644 demo/DemoClassification.ipynb delete mode 100644 demo/DemoClassificationDisc.ipynb delete mode 100644 demo/README.md delete mode 100644 demo/demo.py diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb deleted file mode 100644 index 0c802f4f..00000000 --- a/demo/DemoClassification.ipynb +++ /dev/null @@ -1,691 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "# PSyKE's demo\n", - "\n", - "Some imports." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "6b710e7c", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.model_selection import train_test_split\n", - "from sklearn.datasets import load_iris\n", - "import pandas as pd\n", - "\n", - "from sklearn.neighbors import KNeighborsClassifier\n", - "from sklearn.metrics import accuracy_score, f1_score\n", - "\n", - "from psyke import Extractor, Clustering, EvaluableModel\n", - "from psyke.extraction.hypercubic.strategy import AdaptiveStrategy\n", - "from psyke.extraction.hypercubic import Grid, FeatureRanker\n", - "from psyke.tuning.orchid import OrCHiD\n", - "from psyke.utils.logic import pretty_theory\n", - "from psyke.utils import Target" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "Import iris dataset separating features and class." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "x, y = load_iris(return_X_y=True, as_frame=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "Rename of the features." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "38d5afb0", - "metadata": {}, - "outputs": [], - "source": [ - "x.columns = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "Replace integer indices with the corresponding string class." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4f807185", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " target\n0 setosa\n1 setosa\n2 setosa\n3 setosa\n4 setosa\n.. ...\n145 virginica\n146 virginica\n147 virginica\n148 virginica\n149 virginica\n\n[150 rows x 1 columns]", - "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
target
0setosa
1setosa
2setosa
3setosa
4setosa
......
145virginica
146virginica
147virginica
148virginica
149virginica
\n

150 rows × 1 columns

\n
" - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = pd.DataFrame(y).replace({\"target\": {0: 'setosa', 1: 'versicolor', 2: 'virginica'}})\n", - "y" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "The final dataset:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "7ac49b4e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " SepalLength SepalWidth PetalLength PetalWidth iris\n0 5.1 3.5 1.4 0.2 setosa\n1 4.9 3.0 1.4 0.2 setosa\n2 4.7 3.2 1.3 0.2 setosa\n3 4.6 3.1 1.5 0.2 setosa\n4 5.0 3.6 1.4 0.2 setosa\n.. ... ... ... ... ...\n145 6.7 3.0 5.2 2.3 virginica\n146 6.3 2.5 5.0 1.9 virginica\n147 6.5 3.0 5.2 2.0 virginica\n148 6.2 3.4 5.4 2.3 virginica\n149 5.9 3.0 5.1 1.8 virginica\n\n[150 rows x 5 columns]", - "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
SepalLengthSepalWidthPetalLengthPetalWidthiris
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa
..................
1456.73.05.22.3virginica
1466.32.55.01.9virginica
1476.53.05.22.0virginica
1486.23.45.42.3virginica
1495.93.05.11.8virginica
\n

150 rows × 5 columns

\n
" - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dataset = x.join(y)\n", - "dataset.columns = [*dataset.columns[:-1], 'iris']\n", - "dataset" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "Split between train and test set in a reproducible way." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "03fc5e2c", - "metadata": {}, - "outputs": [], - "source": [ - "train, test = train_test_split(dataset, test_size=0.25, random_state=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "We use as predictor a KNN and we train it." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "aa8a3128", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Accuracy: 0.97\n", - "F1: 0.97\n" - ] - } - ], - "source": [ - "#predictor = MLPClassifier(alpha=1, max_iter=1000)\n", - "predictor = KNeighborsClassifier(n_neighbors=7)\n", - "#predictor = DecisionTreeClassifier()\n", - "predictor.fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "print(f'Accuracy: {accuracy_score(predictor.predict(test.iloc[:, :-1]), test.iloc[:, -1]):.2f}')\n", - "print(f'F1: {f1_score(predictor.predict(test.iloc[:, :-1]), test.iloc[:, -1], average=\"weighted\"):.2f}')" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [], - "source": [ - "def print_scores(scores):\n", - " print(f'Classification accuracy = {scores[EvaluableModel.ClassificationScore.ACCURACY][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.ACCURACY][1]:.2f} (BB)\\n'\n", - " f'F1 = {scores[EvaluableModel.ClassificationScore.F1][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.F1][1]:.2f} (BB)')\n", - "\n", - "def get_scores(extractor, test, predictor, brute=False, criterion='density', n=2):\n", - " return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "We create an extractor that uses the CART algorithm and we extract prolog rules from our trained KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 9, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "CART extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength =< 2.6.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength =< 4.75.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 10, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DiViNE performance (3 rules with 73.68% coverage):\n", - "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "\n", - "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "DiViNE extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" - ] - } - ], - "source": [ - "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=True)\n", - "theory_from_divine = divine.extract(train)\n", - "scores, completeness = get_scores(divine, test, predictor)\n", - "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(divine, test, predictor, True, 'density')\n", - "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 11, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DiViNE performance (3 rules with 73.68% coverage):\n", - "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "\n", - "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "DiViNE extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" - ] - } - ], - "source": [ - "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=False)\n", - "theory_from_divine = divine.extract(train)\n", - "scores, completeness = get_scores(divine, test, predictor)\n", - "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(divine, test, predictor, True)\n", - "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 12, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ITER performance (3 rules with 97.37% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "ITER brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "ITER extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 2.58], PetalWidth in [0.09, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [2.58, 4.94], PetalWidth in [0.09, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [4.94, 6.90], PetalWidth in [0.09, 2.50].\n" - ] - } - ], - "source": [ - "it = Extractor.iter(predictor, min_update=0.2, min_examples=150, threshold=0.1,\n", - " max_iterations=100, n_points=1, fill_gaps=True)\n", - "theory_from_iter = it.extract(train)\n", - "scores, completeness = get_scores(it, test, predictor)\n", - "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(it, test, predictor, True)\n", - "print(f'\\nITER brute performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "We create a GridEx extractor to extract prolog rules from the same KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 13, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GridEx performance (5 rules with 94.74% coverage):\n", - "Classification accuracy = 0.94 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "GridEx brute performance (5 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [4.54, 5.72], PetalWidth in [1.06, 1.54].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 2.17], PetalWidth in [0.09, 1.06].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [2.17, 4.54], PetalWidth in [0.57, 1.06].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.36, 4.54], PetalWidth in [1.06, 2.02].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.54, 6.90], PetalWidth in [1.54, 2.50].\n" - ] - } - ], - "source": [ - "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", - "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.7, 5)])), threshold=.1, min_examples=1)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(gridEx, test, predictor, True)\n", - "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 41, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GridEx performance (3 rules with 94.74% coverage):\n", - "Classification accuracy = 0.92 (data), 0.94 (BB)\n", - "F1 = 0.92 (data), 0.95 (BB)\n", - "\n", - "GridEx brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.92 (data), 0.95 (BB)\n", - "F1 = 0.92 (data), 0.95 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 2.47].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.21, 4.68].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.68, 6.90].\n" - ] - } - ], - "source": [ - "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", - "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(gridEx, test, predictor, True)\n", - "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "We use the CReEPy clustering-based extractor to perform the extraction." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "def print_clustering_scores(scores):\n", - " print(f'ARI = {scores[EvaluableModel.ClusteringScore.ARI][0]:.2f}\\n'\n", - " f'AMI = {scores[EvaluableModel.ClusteringScore.AMI][0]:.2f}\\n'\n", - " f'V-measure = {scores[EvaluableModel.ClusteringScore.V][0]:.2f}\\n'\n", - " f'FMI = {scores[EvaluableModel.ClusteringScore.FMI][0]:.2f}')\n", - "\n", - "def get_clustering_scores(clustering, test):\n", - " return clustering.score(test, None, False, True, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClusteringScore.ARI, EvaluableModel.ClusteringScore.AMI,\n", - " EvaluableModel.ClusteringScore.V, EvaluableModel.ClusteringScore.FMI])\n", - "\n", - "def print_scores_short(scores):\n", - " print(f'Classification accuracy = {scores[EvaluableModel.ClassificationScore.ACCURACY][0]:.2f}')\n", - "\n", - "def get_scores_short(extractor, test):\n", - " return extractor.score(test, None, False, True, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.ExACT, output=Target.CLASSIFICATION,\n", - " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", - "orchid.search()\n", - "(_, _, depth, threshold) = orchid.get_best()[0]" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "exact = Clustering.exact(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", - "exact.fit(train)\n", - "scores, completeness = get_clustering_scores(exact, test)\n", - "print(f'ExACT performance ({exact.n_rules} clusters with {completeness * 100:.2f}% coverage):')\n", - "print_clustering_scores(scores)\n", - "scores, _ = get_scores_short(exact, test)\n", - "print_scores_short(scores)\n", - "print()\n", - "exact.explain()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", - " ranks=ranked, ignore_threshold=.99, clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.CREAM, output=Target.CLASSIFICATION,\n", - " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", - "orchid.search()\n", - "(_, _, depth, threshold) = orchid.get_best()[0]" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "cream = Clustering.cream(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", - "cream.fit(train)\n", - "scores, completeness = get_clustering_scores(cream, test)\n", - "print(f'CREAM performance ({cream.n_rules} clusters with {completeness * 100:.2f}% coverage):')\n", - "print_clustering_scores(scores)\n", - "scores, _ = get_scores_short(cream, test)\n", - "print_scores_short(scores)\n", - "print()\n", - "cream.explain()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", - " ranks=ranked, ignore_threshold=.99, clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/demo/DemoClassificationDisc.ipynb b/demo/DemoClassificationDisc.ipynb deleted file mode 100644 index c240351b..00000000 --- a/demo/DemoClassificationDisc.ipynb +++ /dev/null @@ -1,452 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# PSyKE's demo\n", - "\n", - "Some imports." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "6b710e7c", - "metadata": {}, - "outputs": [], - "source": [ - "from psyke.utils.dataframe import get_discrete_features_supervised, get_discrete_dataset\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.datasets import load_iris\n", - "import pandas as pd\n", - "\n", - "from sklearn.neighbors import KNeighborsClassifier\n", - "from sklearn.tree import DecisionTreeClassifier\n", - "from psyke.extraction.cart.predictor import CartPredictor\n", - "\n", - "from psyke import Extractor, EvaluableModel\n", - "from psyke.utils.logic import pretty_theory" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Import iris dataset separating features and class." - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "x, y = load_iris(return_X_y=True, as_frame=True)" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Rename of the features." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "38d5afb0", - "metadata": {}, - "outputs": [], - "source": [ - "x.columns = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']" - ] - }, - { - "cell_type": "markdown", - "source": [ - "The original features' dataset is discretized using the equal frequency method. Each feature is mapped in a 3 (can be an arbitrary integer) new one-hot encoded sub-features representing 3 real intervals. So from the original 4 features we have a new 12 features dataset. S, M and L stand for small, medium and large." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "423ff1b4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SepalLength = {'SepalLength_0' if SepalLength ∈ ]-∞, 5.39], 'SepalLength_1' if SepalLength ∈ [5.39, 6.26[, 'SepalLength_2' if SepalLength ∈ ]6.26, ∞[}\n", - "\n", - "SepalWidth = {'SepalWidth_0' if SepalWidth ∈ ]-∞, 2.87], 'SepalWidth_1' if SepalWidth ∈ [2.87, 3.20[, 'SepalWidth_2' if SepalWidth ∈ ]3.20, ∞[}\n", - "\n", - "PetalLength = {'PetalLength_0' if PetalLength ∈ ]-∞, 2.27], 'PetalLength_1' if PetalLength ∈ [2.27, 4.87[, 'PetalLength_2' if PetalLength ∈ ]4.87, ∞[}\n", - "\n", - "PetalWidth = {'PetalWidth_0' if PetalWidth ∈ ]-∞, 0.65], 'PetalWidth_1' if PetalWidth ∈ [0.65, 1.64[, 'PetalWidth_2' if PetalWidth ∈ ]1.64, ∞[}\n", - "\n" - ] - } - ], - "source": [ - "iris_features = get_discrete_features_supervised(x.join(y))\n", - "\n", - "for descrete_feature in iris_features:\n", - " print(str(descrete_feature), end='\\n\\n')" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Reassign features' data to the discretized one." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "ffc1852e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " PetalLength_0 PetalLength_1 PetalLength_2 PetalWidth_0 PetalWidth_1 \\\n0 1 0 0 1 0 \n1 1 0 0 1 0 \n2 1 0 0 1 0 \n3 1 0 0 1 0 \n4 1 0 0 1 0 \n.. ... ... ... ... ... \n145 0 0 1 0 0 \n146 0 0 1 0 0 \n147 0 0 1 0 0 \n148 0 0 1 0 0 \n149 0 0 1 0 0 \n\n PetalWidth_2 SepalLength_0 SepalLength_1 SepalLength_2 SepalWidth_0 \\\n0 0 1 0 0 0 \n1 0 1 0 0 0 \n2 0 1 0 0 0 \n3 0 1 0 0 0 \n4 0 1 0 0 0 \n.. ... ... ... ... ... \n145 1 0 0 1 0 \n146 1 0 0 1 1 \n147 1 0 0 1 0 \n148 1 0 1 0 0 \n149 1 0 1 0 0 \n\n SepalWidth_1 SepalWidth_2 \n0 0 1 \n1 1 0 \n2 1 0 \n3 1 0 \n4 0 1 \n.. ... ... \n145 1 0 \n146 0 0 \n147 1 0 \n148 0 1 \n149 1 0 \n\n[150 rows x 12 columns]", - "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
PetalLength_0PetalLength_1PetalLength_2PetalWidth_0PetalWidth_1PetalWidth_2SepalLength_0SepalLength_1SepalLength_2SepalWidth_0SepalWidth_1SepalWidth_2
0100100100001
1100100100010
2100100100010
3100100100010
4100100100001
.......................................
145001001001010
146001001001100
147001001001010
148001001010001
149001001010010
\n

150 rows × 12 columns

\n
" - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = get_discrete_dataset(x, iris_features)\n", - "x" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Replace integer indices with the corresponding string class." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "4f807185", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " target\n0 setosa\n1 setosa\n2 setosa\n3 setosa\n4 setosa\n.. ...\n145 virginica\n146 virginica\n147 virginica\n148 virginica\n149 virginica\n\n[150 rows x 1 columns]", - "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
target
0setosa
1setosa
2setosa
3setosa
4setosa
......
145virginica
146virginica
147virginica
148virginica
149virginica
\n

150 rows × 1 columns

\n
" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = pd.DataFrame(y).replace({\"target\": {0: 'setosa', 1: 'versicolor', 2: 'virginica'}})\n", - "y" - ] - }, - { - "cell_type": "markdown", - "source": [ - "The final dataset:" - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "7ac49b4e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " PetalLength_0 PetalLength_1 PetalLength_2 PetalWidth_0 PetalWidth_1 \\\n0 1 0 0 1 0 \n1 1 0 0 1 0 \n2 1 0 0 1 0 \n3 1 0 0 1 0 \n4 1 0 0 1 0 \n.. ... ... ... ... ... \n145 0 0 1 0 0 \n146 0 0 1 0 0 \n147 0 0 1 0 0 \n148 0 0 1 0 0 \n149 0 0 1 0 0 \n\n PetalWidth_2 SepalLength_0 SepalLength_1 SepalLength_2 SepalWidth_0 \\\n0 0 1 0 0 0 \n1 0 1 0 0 0 \n2 0 1 0 0 0 \n3 0 1 0 0 0 \n4 0 1 0 0 0 \n.. ... ... ... ... ... \n145 1 0 0 1 0 \n146 1 0 0 1 1 \n147 1 0 0 1 0 \n148 1 0 1 0 0 \n149 1 0 1 0 0 \n\n SepalWidth_1 SepalWidth_2 iris \n0 0 1 setosa \n1 1 0 setosa \n2 1 0 setosa \n3 1 0 setosa \n4 0 1 setosa \n.. ... ... ... \n145 1 0 virginica \n146 0 0 virginica \n147 1 0 virginica \n148 0 1 virginica \n149 1 0 virginica \n\n[150 rows x 13 columns]", - "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
PetalLength_0PetalLength_1PetalLength_2PetalWidth_0PetalWidth_1PetalWidth_2SepalLength_0SepalLength_1SepalLength_2SepalWidth_0SepalWidth_1SepalWidth_2iris
0100100100001setosa
1100100100010setosa
2100100100010setosa
3100100100010setosa
4100100100001setosa
..........................................
145001001001010virginica
146001001001100virginica
147001001001010virginica
148001001010001virginica
149001001010010virginica
\n

150 rows × 13 columns

\n
" - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dataset = x.join(y)\n", - "dataset.columns = [*dataset.columns[:-1], 'iris']\n", - "dataset" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Split between train and test set in a reproducible way." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "03fc5e2c", - "metadata": {}, - "outputs": [], - "source": [ - "train, test = train_test_split(dataset, test_size=0.5, random_state=0)" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We use as predictor a KNN with K = 4 and we train it." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "aa8a3128", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": "0.9333333333333333" - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "predictor = KNeighborsClassifier(n_neighbors=4)\n", - "predictor.fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "predictor.score(test.iloc[:, :-1], test.iloc[:, -1])" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We create an extractor that uses the REAL algorithm and we extract prolog rules from our trained KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 19, - "outputs": [], - "source": [ - "def print_scores(scores):\n", - " print(f'Classification accuracy = {scores[EvaluableModel.ClassificationScore.ACCURACY][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.ACCURACY][1]:.2f} (BB)\\n'\n", - " f'F1 = {scores[EvaluableModel.ClassificationScore.F1][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.F1][1]:.2f} (BB)')\n", - "\n", - "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "5e97565d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "REAL performance (8 rules with 94.67% coverage):\n", - "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "REAL extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalWidth =< 0.64, SepalLength =< 5.38.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalWidth =< 0.64, SepalWidth > 3.20, SepalLength =< 6.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalWidth in [0.64, 1.63], SepalWidth =< 2.87, PetalLength > 2.26, SepalLength =< 6.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalWidth in [0.64, 1.63], SepalLength in [5.38, 6.26], SepalWidth =< 3.20.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalWidth in [0.64, 1.63], PetalLength =< 4.86, SepalLength > 5.38, SepalWidth =< 3.20.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [5.38, 6.26], SepalWidth in [2.87, 3.20], PetalLength =< 4.86, PetalWidth > 0.64.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength > 4.86, SepalLength > 6.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength > 4.86, PetalWidth > 1.63.\n" - ] - } - ], - "source": [ - "real = Extractor.real(predictor, iris_features)\n", - "theory_from_real = real.extract(train)\n", - "scores, completeness = get_scores(real, test, predictor)\n", - "print(f'REAL performance ({real.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('REAL extracted rules:\\n\\n' + pretty_theory(theory_from_real))" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We create a different extractor that use Trepan algorithm and we extract prolog rules from the same KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "dc20410e", - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "TREPAN performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.96 (BB)\n", - "F1 = 0.95 (data), 0.96 (BB)\n", - "\n", - "Trepan extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength > 2.26, PetalLength in [2.26, 4.86].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength > 2.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa).\n" - ] - } - ], - "source": [ - "trepan = Extractor.trepan(predictor, iris_features)\n", - "theory_from_trepan = trepan.extract(train)\n", - "scores, completeness = get_scores(trepan, test, predictor)\n", - "print(f'TREPAN performance ({trepan.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nTrepan extracted rules:\\n\\n' + pretty_theory(theory_from_trepan))" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We create another different extractor that use CART algorithm." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 22, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.96 (BB)\n", - "F1 = 0.95 (data), 0.96 (BB)\n", - "\n", - "CART extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalWidth =< 0.64.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength not_in [2.26, 4.86].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, discretization=iris_features, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 98331ff5..28f7f265 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -1,15 +1,5 @@ { "cells": [ - { - "cell_type": "markdown", - "id": "f52126f3", - "metadata": {}, - "source": [ - "# PSyKE's demo for regression tasks\n", - "\n", - "Some imports." - ] - }, { "cell_type": "code", "execution_count": 1, @@ -17,153 +7,78 @@ "metadata": {}, "outputs": [], "source": [ - "from psyke import Extractor, Clustering\n", - "from psyke.tuning.pedro import PEDRO\n", - "from psyke.tuning import Objective\n", - "from psyke.tuning.crash import CRASH\n", - "from sklearn.tree import DecisionTreeRegressor\n", - "from psyke.utils.logic import pretty_theory\n", - "from psyke.utils.metrics import mae, mse, r2\n", - "from sklearn.model_selection import train_test_split\n", + "from sklearn.neighbors import KNeighborsRegressor as KNN\n", "from sklearn.preprocessing import StandardScaler\n", - "from psyke.utils import Target\n", - "import pandas as pd" - ] - }, - { - "cell_type": "markdown", - "id": "d7c90ed2", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "Import an artificial dataset." + "\n", + "from joblib import dump, load\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "from psyke import Extractor, Clustering, Target\n", + "from psyke.extraction.hypercubic.strategy import AdaptiveStrategy\n", + "from psyke.extraction.hypercubic import Grid, FeatureRanker\n", + "from psyke.utils.logic import pretty_theory" ] }, { "cell_type": "code", "execution_count": 2, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "#dataset = pd.read_csv(\"../test/resources/datasets/df.csv\")\n", - "#dataset = dataset[[\"X\", \"Y\", \"Z4\"]].dropna()\n", - "#dataset = pd.read_csv(\"../test/resources/datasets/CCPP.csv\", sep=\";\", decimal=\",\")\n", - "dataset = pd.read_csv(\"../test/resources/datasets/arti.csv\")" - ] - }, - { - "cell_type": "markdown", - "id": "d673b766", - "metadata": {}, - "source": [ - "Split between train and test set in a reproducible way." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "03fc5e2c", - "metadata": {}, "outputs": [], "source": [ - "train, test = train_test_split(dataset, test_size=0.5, random_state=10)\n", + "def getTrainTest(data, testB):\n", + " b = bartels[bartels.n==testB]\n", + " t0, t1 = b.t0.values[0], b.t1.values[0]\n", + " idx = (data.index >= t0) & (data.index < t1)\n", + " return data[~idx], data[idx]\n", "\n", - "scaler = StandardScaler().fit(train)\n", - "train = pd.DataFrame(scaler.transform(train), columns=train.columns, index=train.index)\n", - "test = pd.DataFrame(scaler.transform(test), columns=test.columns, index=test.index)\n", - "\n", - "normalization = {key: (m, s) for key, m, s in zip(train.columns, scaler.mean_, scaler.scale_)}" - ] - }, - { - "cell_type": "markdown", - "id": "fa6754a0", - "metadata": {}, - "source": [ - "We select and train a predictor." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "bed764ca", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MAE = 0.00\n", - "MSE = 0.00\n", - "R2 = 1.00\n" - ] - } + "def getScaler(train, name):\n", + " scaler = StandardScaler().fit(train)\n", + " dump(scaler, f\"scalers/scalerV{name}.joblib\")\n", + " normalization = {key: (m, s) for key, m, s in zip(train.columns, scaler.mean_, scaler.scale_)}\n", + " return scaler, pd.DataFrame(scaler.transform(train), columns=train.columns), normalization" ], - "source": [ - "#predictor = KNeighborsRegressor(n_neighbors=3).fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "predictor = DecisionTreeRegressor().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "#predictor = LinearRegression().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "\n", - "m, s = normalization[test.columns[-1]]\n", - "\n", - "predicted = predictor.predict(test.iloc[:, :-1]).flatten() * s + m\n", - "true = test.iloc[:, -1] * s + m\n", - "\n", - "print(f'MAE = {mae(true, predicted):.2f}')\n", - "print(f'MSE = {mse(true, predicted):.2f}')\n", - "print(f'R2 = {r2(true, predicted):.2f}')" - ] - }, - { - "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { - "name": "#%% md\n" + "name": "#%%\n" } - }, - "source": [ - "We define a function to print the extractors’ evaluation" - ] + } }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "execution_count": 3, "outputs": [], "source": [ - "def evaluate(name, extractor, true, predicted):\n", - " extracted = extractor.unscale(extractor.predict(test.iloc[:, :-1]), test.columns[-1])\n", - " print(f'{name} performance ({extractor.n_rules} rules):\\n'\n", - " f'MAE = {mae(true, extracted):.2f}\\nMAE fidelity = {mae(predicted, extracted):.2f}\\n'\n", - " f'R2 = {r2(true, extracted):.2f}\\nR2 fidelity = {r2(predicted, extracted):.2f}\\n')" - ] - }, - { - "cell_type": "markdown", - "id": "96835867", - "metadata": {}, - "source": [ - "We create several extractors that use ITER, GridEx and GridREx algorithms to extract prolog rules from the predictor." - ] - }, - { - "cell_type": "code", - "execution_count": 6, + "def gridrex(model, train, test, normalization, s, m):\n", + " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", + " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n", + " threshold=5, min_examples=1, normalization=normalization)\n", + " gridREx.extract(train)\n", + " return gridREx.brute_predict(test) * s + m, gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", + "\n", + "def cart(model, train, test, normalization, s, m):\n", + " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", + " CART.extract(train)\n", + " return CART.predict(test) * s + m, CART.n_rules, 0\n", + "\n", + "def cosmik(model, train, test, normalization, s, m):\n", + " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", + " output=Target.CONSTANT, normalization=normalization)\n", + " COSMiK.extract(train)\n", + " return COSMiK.brute_predict(test) * s + m, COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", + "\n", + "def creepy(model, train, test, normalization, s, m):\n", + " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", + " output=Target.CONSTANT, normalization=normalization)\n", + " CReEPy.extract(train)\n", + " return CReEPy.brute_predict(test) * s + m, CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } +<<<<<<< HEAD }, "outputs": [ { @@ -196,787 +111,158 @@ "evaluate('ITER', it, true, predicted)\n", "print('ITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" ] +======= + } +>>>>>>> wip }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "execution_count": 5, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "CReEPy performance (3 rules):\n", - "MAE = 0.08\n", - "MAE fidelity = 0.08\n", - "R2 = 0.81\n", - "R2 fidelity = 0.81\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.99].\n", - "z(X, Y, 0.145238).\n" + "2491 CART\rEx2491 CReEPy\r" + ] + }, + { + "ename": "TypeError", + "evalue": "'function' object is not subscriptable", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/2296165336.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/1333199017.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 20\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n\u001B[0;32m 21\u001B[0m output=Target.CONSTANT, normalization=normalization)\n\u001B[1;32m---> 22\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 23\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\creepy\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdimension\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdimension\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 39\u001B[1;33m \u001B[0mtheory\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[0mlast_clause\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclauses\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mretract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mlast_clause\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_create_theory\u001B[1;34m(self, dataframe, sort)\u001B[0m\n\u001B[0;32m 118\u001B[0m \u001B[0mvariables\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mto_var\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 119\u001B[0m head = HyperCubeExtractor._create_head(dataframe, list(variables.values()),\n\u001B[1;32m--> 120\u001B[1;33m self.unscale(cube.output, dataframe.columns[-1]))\n\u001B[0m\u001B[0;32m 121\u001B[0m \u001B[0mbody\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbody\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mvariables\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0munscale\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[0mnew_theory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0massertZ\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mclause\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mhead\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbody\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36munscale\u001B[1;34m(self, values, name)\u001B[0m\n\u001B[0;32m 76\u001B[0m value * self.normalization[name][1] + self.normalization[name][0] for value in values]\n\u001B[0;32m 77\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 78\u001B[1;33m \u001B[0mvalues\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mvalues\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 79\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mvalues\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 80\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mTypeError\u001B[0m: 'function' object is not subscriptable" ] } ], "source": [ - "creepy = Extractor.creepy(predictor, depth=3, error_threshold=0.02, output=Target.CONSTANT,\n", - " normalization=normalization, clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 8, + "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", + "\n", + "TESTB = [i for i in range(2491, 2509)]\n", + "\n", + "predicted = {'index': [], 'V': [], 'model': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "\n", + "rules = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "\n", + "missed = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "\n", + "for testB in TESTB:\n", + " rules['BR'].append(testB)\n", + " missed['BR'].append(testB)\n", + " print(testB, end='\\r')\n", + "\n", + " data = pd.read_csv(f'data/halffuzzycoefs2B.csv', parse_dates=[0], index_col=0)\n", + " train, test = getTrainTest(data, testB)\n", + "\n", + " predicted['index'] += list(test.index.values)\n", + " predicted['V'] += list(test.V.values)\n", + "\n", + " scaler, scaledTrain, normalization = getScaler(train, f\"test{testB}\")\n", + " scaledTest = pd.DataFrame(scaler.transform(test), columns=test.columns).iloc[:, :-1]\n", + " m, s = normalization[test.columns[-1]]\n", + "\n", + " model = KNN(200, weights='distance', p=1).fit(scaledTrain.iloc[:, :-1], scaledTrain.iloc[:, -1])\n", + " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", + " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", + "\n", + " for name, fun in zip(['GridREx', 'CART', 'CReEPy'], [gridrex, cart, creepy]):\n", + " print(testB, name, end='\\r')\n", + " #if name in ['GridREx', 'CART', 'COSMiK']:\n", + " # continue\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", + " predicted[name] += list(pred)\n", + " rules[name].append(n)\n", + " missed[name].append(miss)" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules):\n", - "MAE = 0.03\n", - "MAE fidelity = 0.03\n", - "R2 = 0.94\n", - "R2 fidelity = 0.94\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.49], Z is 0.7.\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.99], Z is 0.4.\n", - "z(X, Y, Z) :-\n", - " Y in [0.00, 0.99], Z is 0.38 - 0.09 * X - 1.73 * Y.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=3, error_threshold=0.02, output=Target.REGRESSION,\n", - " normalization=normalization, clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] + } }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, + "outputs": [], + "source": [ + "pd.DataFrame(predicted)" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (4 rules):\n", - "MAE = 0.01\n", - "MAE fidelity = 0.01\n", - "R2 = 0.94\n", - "R2 fidelity = 0.94\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [0.51, 0.99], Y in [0.00, 0.49], Z is 0.3.\n", - "z(X, Y, Z) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.98], Z is 0.0.\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.49], Y in [0.50, 0.99], Z is 0.4.\n", - "z(X, Y, Z) :-\n", - " Y in [0.00, 0.99], Z is 0.7.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.02, output=Target.REGRESSION,\n", - " normalization=normalization, clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] + } }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, + "outputs": [], + "source": [ + "pd.DataFrame(rules)" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "\n", - "**********************\n", - "Best Algorithm.ExACT\n", - "**********************\n", - "MAE = 0.06, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.06, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.06, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "CReEPy performance (3 rules):\n", - "MAE = 0.69\n", - "MAE fidelity = 0.69\n", - "R2 = -8.82\n", - "R2 fidelity = -8.82\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [-1.70, 0.01], Y in [-1.69, 0.02], Z is 1.37.\n", - "z(X, Y, Z) :-\n", - " X in [-1.70, 0.01], Y in [-1.69, 1.75], Z is 0.19.\n", - "z(X, Y, Z) :-\n", - " Y in [-1.69, 1.75], Z is -0.76 - 0.02 * X - 0.50 * Y.\n" - ] - } - ], - "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.5,\n", - " algorithm=CRASH.Algorithm.ExACT, output=Target.REGRESSION, normalization=normalization)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] + } }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "execution_count": 6, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 0.12, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 0.12, 2 rules\n", - "\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 0.02, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 0.02, 3 rules\n", - "\n", - "**********************\n", - "Best Algorithm.CREAM\n", - "**********************\n", - "MAE = 0.02, 3 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.02, 3 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.12, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 1\n", - "\n", - "CReEPy performance (4 rules):\n", - "MAE = 0.69\n", - "MAE fidelity = 0.69\n", - "R2 = -9.40\n", - "R2 fidelity = -9.40\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [0.07, 1.75], Y in [-1.69, 0.00], Z is -0.19.\n", - "z(X, Y, Z) :-\n", - " X in [0.02, 1.75], Y in [-1.69, 1.71], Z is -1.37.\n", - "z(X, Y, Z) :-\n", - " X in [-1.69, 0.01], Y in [0.03, 1.75], Z is 0.19.\n", - "z(X, Y, Z) :-\n", - " Y in [-1.69, 1.75], Z is 1.37.\n" - ] + "data": { + "text/plain": "{'Bmin': (2.909256272401434, 0.9536815020973909),\n 'Bmedian': (5.282275985663082, 1.4553994817336167),\n 'Bmax': (10.302267025089607, 4.267392124190232),\n 'Btrend': (0.0003254668720864083, 0.05779357910523391),\n 'Bhalf1': (2.92125578628164e-05, 0.10419543281233311),\n 'Bhalf2': (0.0005042227155979311, 0.10530984379227255),\n 'GCRmin': (-3.524847659251415, 1.5620135919428861),\n 'GCRmedian': (-0.013669578853476324, 1.6346771275509167),\n 'GCRmax': (3.732416265082763, 2.1377962993128765),\n 'GCRtrend': (-2.9804500565074855e-05, 0.019833671296497202),\n 'GCRhalf1': (0.0002589125691562307, 0.03025486059161083),\n 'GCRhalf2': (-0.00023048455270716005, 0.030366014637694866),\n 'V': (453.97293906810035, 108.90956420129501)}" + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.75, algorithm=CRASH.Algorithm.CREAM,\n", - " normalization=normalization)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, + "normalization" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 0.00, 4 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 0.00, 8 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 0.00, 17 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 0.00, 26 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 0.00, 28 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 0.00, 30 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 0.00, 33 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 0.00, 36 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 0.00, 41 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 0.00, 46 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 0.00, 56 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 0.00, 66 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 0.00, 72 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 0.00, 78 rules\n", - "\n", - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.00, 72 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridEx performance (82 rules):\n", - "MAE = 0.00\n", - "MAE fidelity = 0.00\n", - "R2 = 0.99\n", - "R2 fidelity = 0.99\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n" - ] - } - ], - "source": [ - "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL,\n", - " normalization=normalization)\n", - "pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridEx = Extractor.gridex(predictor, grid, threshold=threshold, normalization=normalization)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "evaluate('GridEx', gridEx, true, predicted)\n", - "print('GridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ] + } }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, + "outputs": [], + "source": [], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.00, 72 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridREx performance (86 rules):\n", - "MAE = 0.00\n", - "MAE fidelity = 0.00\n", - "R2 = 0.99\n", - "R2 fidelity = 0.99\n", - "\n", - "GridREx extracted rules:\n", - "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49], Z is 0.7.\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99], Z is 0.40 - 0.07 * X + 0.00 * Y.\n", - "z(X, Y, Z) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49], Z is 0.3.\n", - "z(X, Y, Z) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99], Z is 0.0.\n" - ] - } - ], - "source": [ - "#pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - "# max_depth=2, patience=1, algorithm=PEDRO.Algorithm.GRIDREX, objective=Objective.MODEL)\n", - "#pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridREx = Extractor.gridrex(predictor, grid, threshold=threshold, normalization=normalization)\n", - "theory_from_gridREx = gridREx.extract(train)\n", - "evaluate('GridREx', gridREx, true, predicted)\n", - "print('GridREx extracted rules:\\n\\n' + pretty_theory(theory_from_gridREx))" - ] + } }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, + "outputs": [], + "source": [], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (4 rules):\n", - "MAE = 0.00\n", - "MAE fidelity = 0.00\n", - "R2 = 1.00\n", - "R2 fidelity = 1.00\n", - "\n", - "CART extracted rules:\n", - "\n", - "z(X, Y, 0.3) :-\n", - " X > 0.50, Y =< 0.49.\n", - "z(X, Y, 0.0) :-\n", - " X > 0.50, Y > 0.49.\n", - "z(X, Y, 0.7) :-\n", - " Y =< 0.50.\n", - "z(X, Y, 0.4).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, max_depth=4, max_leaves=4, simplify=True, normalization=normalization)\n", - "theory_from_cart = cart.extract(train)\n", - "evaluate('CART', cart, true, predicted)\n", - "print('CART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ] + } } ], "metadata": { diff --git a/demo/README.md b/demo/README.md deleted file mode 100644 index a67d14ac..00000000 --- a/demo/README.md +++ /dev/null @@ -1,40 +0,0 @@ -## About Notebooks files in this directory - -Notebook files (`.ipynb`) in this directory contain demos of PSyKE. - -These are NOT meant to be executed directly, but rather via some container started from the [PSyKE Image](https://hub.docker.com/r/pikalab/psyke) on DockerHub. - -To do so, please ensure -- [Docker](https://docs.docker.com/engine/install/) is installed on your system -- the Docker service is up and running - -and then follow these steps: - -1. Choose a target version of PSyKE, say `X.Y.Z` - - cf. [DockerHub image tags](https://hub.docker.com/r/pikalab/psyke/tags) - - cf. [PyPi releases history](https://pypi.org/project/psyke/#history) - - cf. [GitHub releases](https://github.com/psykei/psyke-python/releases) - -2. Pull the corresponding image: - ```bash - docker pull pikalab/psyke:X.Y.Z - ``` - -2. Run the image into a container: - ```bash - docker run --rm -it -p 8888:8888 --name psyke pikalab/psyke:X.Y.Z - ``` - -3. Some logs such as the following ones should eventually be printed - ``` - To access the notebook, open this file in a browser: - file:///root/.local/share/jupyter/runtime/nbserver-7-open.html - Or copy and paste one of these URLs: - http://66fa9b93bbe7:8888/?token= - or http://127.0.0.1:8888/?token= - ``` - -4. Open your browser and browse to `http://localhost:8888/?token=` - - use the token from the logs above, if requested by the Web page - -5. Enjoy PSyKE-powered notebook! diff --git a/demo/demo.py b/demo/demo.py deleted file mode 100644 index c4d2d124..00000000 --- a/demo/demo.py +++ /dev/null @@ -1,27 +0,0 @@ -from sklearn.datasets import load_iris -from sklearn.model_selection import train_test_split -from sklearn.neighbors import KNeighborsClassifier -import pandas as pd -from psyke import Extractor -from psyke.utils.dataframe import get_discrete_features_equal_frequency, get_discrete_dataset -from psyke.utils.logic import pretty_theory - -x, y = load_iris(return_X_y=True, as_frame=True) -x.columns = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'] -iris_features = get_discrete_features_equal_frequency(x, bins=3, output=False) -x = get_discrete_dataset(x, iris_features) -y = pd.DataFrame(y).replace({"target": {0: 'setosa', 1: 'virginica', 2: 'versicolor'}}) -dataset = x.join(y) -dataset.columns = [*dataset.columns[:-1], 'iris'] -train, test = train_test_split(dataset, test_size=0.5, random_state=0) - -predictor = KNeighborsClassifier(n_neighbors=7) -predictor.fit(train.iloc[:, :-1], train.iloc[:, -1]) - -real = Extractor.real(predictor, iris_features) -theory_from_real = real.extract(train) -print('REAL extracted rules:\n' + pretty_theory(theory_from_real)) - -trepan = Extractor.trepan(predictor, iris_features) -theory_from_trepan = trepan.extract(train) -print('\nTrepan extracted rules:\n' + pretty_theory(theory_from_trepan)) From 38eb3aef1e5a98e7aea97d4f397ad1249f13a6c4 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:27:54 +0200 Subject: [PATCH 19/67] wip --- demo/DemoRegressionScaled.ipynb | 49 +++++---------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 28f7f265..6a834e0e 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -56,64 +56,29 @@ " gridREx.extract(train)\n", " return gridREx.brute_predict(test) * s + m, gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", - "def cart(model, train, test, normalization, s, m):\n", + "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", " CART.extract(train)\n", - " return CART.predict(test) * s + m, CART.n_rules, 0\n", + " return CART.predict(test), CART.n_rules, 0\n", "\n", - "def cosmik(model, train, test, normalization, s, m):\n", + "def cosmik(model, train, test, normalization):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", " output=Target.CONSTANT, normalization=normalization)\n", " COSMiK.extract(train)\n", - " return COSMiK.brute_predict(test) * s + m, COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", + " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization, s, m):\n", + "def creepy(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", " output=Target.CONSTANT, normalization=normalization)\n", " CReEPy.extract(train)\n", - " return CReEPy.brute_predict(test) * s + m, CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" + " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } -<<<<<<< HEAD - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ITER performance (4 rules):\n", - "MAE = 0.55\n", - "MAE fidelity = 0.55\n", - "R2 = -6.34\n", - "R2 fidelity = -6.34\n", - "\n", - "ITER extracted rules:\n", - "\n", - "z(X, Y, 1.11) :-\n", - " X in [-1.70, -0.00], Y in [-1.69, 0.22].\n", - "z(X, Y, 0.19) :-\n", - " X in [-1.70, -0.00], Y in [0.22, 1.75].\n", - "z(X, Y, -0.11) :-\n", - " X in [-0.00, 1.75], Y in [-1.69, 0.22].\n", - "z(X, Y, -1.29) :-\n", - " X in [-0.00, 1.75], Y in [0.22, 1.75].\n" - ] - } - ], - "source": [ - "it = Extractor.iter(predictor, min_update=1.0 / 10, n_points=1, max_iterations=600,\n", - " min_examples=100, threshold=.4, normalization=normalization)\n", - "theory_from_iter = it.extract(train)\n", - "evaluate('ITER', it, true, predicted)\n", - "print('ITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ] -======= } ->>>>>>> wip }, { "cell_type": "code", @@ -177,7 +142,7 @@ " print(testB, name, end='\\r')\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", - " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", " missed[name].append(miss)" From 651ca7457e53fb4508ff63902ec565010716d64c Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:00:18 +0200 Subject: [PATCH 20/67] wip --- demo/DemoRegressionScaled.ipynb | 61 ++++++++++++--------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 6a834e0e..79d9abc1 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -49,12 +49,12 @@ "execution_count": 3, "outputs": [], "source": [ - "def gridrex(model, train, test, normalization, s, m):\n", + "def gridrex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", - " return gridREx.brute_predict(test) * s + m, gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", + " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", @@ -63,13 +63,13 @@ "\n", "def cosmik(model, train, test, normalization):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", - " output=Target.CONSTANT, normalization=normalization)\n", + " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", "def creepy(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", - " output=Target.CONSTANT, normalization=normalization)\n", + " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" ], @@ -88,23 +88,29 @@ "name": "stdout", "output_type": "stream", "text": [ - "2491 CART\rEx2491 CReEPy\r" + "2491 CART\rEx2491 COSMiK\r" ] }, { - "ename": "TypeError", - "evalue": "'function' object is not subscriptable", + "ename": "KeyboardInterrupt", + "evalue": "", "output_type": "error", "traceback": [ "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/2296165336.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/1333199017.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 20\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n\u001B[0;32m 21\u001B[0m output=Target.CONSTANT, normalization=normalization)\n\u001B[1;32m---> 22\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 23\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\creepy\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdimension\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdimension\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 39\u001B[1;33m \u001B[0mtheory\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[0mlast_clause\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclauses\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mretract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mlast_clause\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_create_theory\u001B[1;34m(self, dataframe, sort)\u001B[0m\n\u001B[0;32m 118\u001B[0m \u001B[0mvariables\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mto_var\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 119\u001B[0m head = HyperCubeExtractor._create_head(dataframe, list(variables.values()),\n\u001B[1;32m--> 120\u001B[1;33m self.unscale(cube.output, dataframe.columns[-1]))\n\u001B[0m\u001B[0;32m 121\u001B[0m \u001B[0mbody\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbody\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mvariables\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0munscale\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[0mnew_theory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0massertZ\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mclause\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mhead\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbody\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36munscale\u001B[1;34m(self, values, name)\u001B[0m\n\u001B[0;32m 76\u001B[0m value * self.normalization[name][1] + self.normalization[name][0] for value in values]\n\u001B[0;32m 77\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 78\u001B[1;33m \u001B[0mvalues\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mvalues\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 79\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mvalues\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 80\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mTypeError\u001B[0m: 'function' object is not subscriptable" + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/2919256357.py\u001B[0m in \u001B[0;36mcosmik\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 15\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[0;32m 16\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 17\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 18\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 19\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mcreepy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mstr\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;34m'corner'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 35\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 36\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " ] } ], @@ -138,7 +144,7 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(['GridREx', 'CART', 'CReEPy'], [gridrex, cart, creepy]):\n", + " for name, fun in zip(['GridREx', 'CART', 'COSMiK', 'CReEPy'], [gridrex, cart, cosmik, creepy]):\n", " print(testB, name, end='\\r')\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", @@ -182,29 +188,6 @@ } } }, - { - "cell_type": "code", - "execution_count": 6, - "outputs": [ - { - "data": { - "text/plain": "{'Bmin': (2.909256272401434, 0.9536815020973909),\n 'Bmedian': (5.282275985663082, 1.4553994817336167),\n 'Bmax': (10.302267025089607, 4.267392124190232),\n 'Btrend': (0.0003254668720864083, 0.05779357910523391),\n 'Bhalf1': (2.92125578628164e-05, 0.10419543281233311),\n 'Bhalf2': (0.0005042227155979311, 0.10530984379227255),\n 'GCRmin': (-3.524847659251415, 1.5620135919428861),\n 'GCRmedian': (-0.013669578853476324, 1.6346771275509167),\n 'GCRmax': (3.732416265082763, 2.1377962993128765),\n 'GCRtrend': (-2.9804500565074855e-05, 0.019833671296497202),\n 'GCRhalf1': (0.0002589125691562307, 0.03025486059161083),\n 'GCRhalf2': (-0.00023048455270716005, 0.030366014637694866),\n 'V': (453.97293906810035, 108.90956420129501)}" - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "normalization" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, { "cell_type": "code", "execution_count": null, From 1f3c64186245eb40b9d995d0efb754995aeafecc Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:25:37 +0200 Subject: [PATCH 21/67] wip --- demo/DemoRegressionScaled.ipynb | 41 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 79d9abc1..d418bba5 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -82,13 +82,13 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2491 CART\rEx2491 COSMiK\r" + "2492 GridREx\r491 COSMiK2492" ] }, { @@ -98,18 +98,27 @@ "traceback": [ "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/2919256357.py\u001B[0m in \u001B[0;36mcosmik\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 15\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[0;32m 16\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 17\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 18\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 19\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mcreepy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mstr\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;34m'corner'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 35\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 36\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/2919256357.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 3\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n\u001B[0;32m 4\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m----> 5\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 6\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0msurrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[0msurrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minit_diversity\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m2\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mthreshold\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0msurrounding\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_iterate\u001B[1;34m(self, surrounding, dataframe)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfake\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mto_split\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mto_split\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_merge\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfake\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[0mnext_iteration\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mto_split\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[0mprev\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mnext_iteration\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_merge\u001B[1;34m(self, to_split, dataframe)\u001B[0m\n\u001B[0;32m 105\u001B[0m \u001B[1;31m# TODO: refactor this. A while true with a break is as ugly as hunger.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 106\u001B[0m \u001B[1;32mwhile\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 107\u001B[1;33m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0m\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 109\u001B[0m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 107\u001B[0m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 109\u001B[1;33m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n\u001B[0m\u001B[0;32m 110\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_merge\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m==\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 111\u001B[0m \u001B[1;32mbreak\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_evaluate_merge\u001B[1;34m(self, not_in_cache, dataframe, feature, cube, other_cube, merge_cache)\u001B[0m\n\u001B[0;32m 94\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mor\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 95\u001B[0m \u001B[0mmerged_cube\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmerge_along_dimension\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfeature\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 96\u001B[1;33m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 97\u001B[0m \u001B[0mmerge_cache\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 98\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCLASSIFICATION\u001B[0m \u001B[1;32melse\u001B[0m\u001B[0;31m \u001B[0m\u001B[0;31m\\\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\hypercube.py\u001B[0m in \u001B[0;36mupdate\u001B[1;34m(self, dataset, predictor)\u001B[0m\n\u001B[0;32m 362\u001B[0m \u001B[0mfiltered\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfilter_dataframe\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataset\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 363\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m \u001B[1;33m>\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 364\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 365\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 366\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_diversity\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mabs\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m-\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmean\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", "\u001B[1;31mKeyboardInterrupt\u001B[0m: " ] } @@ -128,7 +137,7 @@ "for testB in TESTB:\n", " rules['BR'].append(testB)\n", " missed['BR'].append(testB)\n", - " print(testB, end='\\r')\n", + " print(testB)\n", "\n", " data = pd.read_csv(f'data/halffuzzycoefs2B.csv', parse_dates=[0], index_col=0)\n", " train, test = getTrainTest(data, testB)\n", @@ -145,7 +154,7 @@ " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", " for name, fun in zip(['GridREx', 'CART', 'COSMiK', 'CReEPy'], [gridrex, cart, cosmik, creepy]):\n", - " print(testB, name, end='\\r')\n", + " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", From 12053d9df53434cd6ab5cc11fcb1d51553e6912d Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:13:35 +0200 Subject: [PATCH 22/67] wip --- demo/DemoRegressionScaled.ipynb | 207 +++++++++++++++++++++++++------- 1 file changed, 161 insertions(+), 46 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index d418bba5..ddf14ec3 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -49,25 +49,32 @@ "execution_count": 3, "outputs": [], "source": [ - "def gridrex(model, train, test, normalization):\n", + "def gridex(model, train, test, normalization, s, m):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", - " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n", + " gridEx = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3), (0.75, 4)])),\n", + " threshold=5, min_examples=1, normalization=normalization)\n", + " gridEx.extract(train)\n", + " return gridEx.brute_predict(test), gridEx.n_rules, sum([p is None for p in gridEx.predict(test)])\n", + " \n", + "def gridrex(model, train, test, normalization, s, m):\n", + " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", + " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", - "def cart(model, train, test, normalization):\n", + "def cart(model, train, test, normalization, s, m):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", " CART.extract(train)\n", - " return CART.predict(test), CART.n_rules, 0\n", + " return CART.predict(test) * s + m, CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", - "def cosmik(model, train, test, normalization):\n", + "def cosmik(model, train, test, normalization, s, m):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", - " output=Target.REGRESSION, normalization=normalization)\n", + " output=Target.CONSTANT, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization):\n", + "def creepy(model, train, test, normalization, s, m):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", @@ -88,38 +95,96 @@ "name": "stdout", "output_type": "stream", "text": [ - "2492 GridREx\r491 COSMiK2492" - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/2919256357.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 3\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n\u001B[0;32m 4\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m----> 5\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 6\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0msurrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[0msurrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minit_diversity\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m2\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mthreshold\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0msurrounding\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_iterate\u001B[1;34m(self, surrounding, dataframe)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfake\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mto_split\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mto_split\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_merge\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfake\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[0mnext_iteration\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mto_split\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[0mprev\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mnext_iteration\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_merge\u001B[1;34m(self, to_split, dataframe)\u001B[0m\n\u001B[0;32m 105\u001B[0m \u001B[1;31m# TODO: refactor this. A while true with a break is as ugly as hunger.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 106\u001B[0m \u001B[1;32mwhile\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 107\u001B[1;33m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0m\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 109\u001B[0m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 107\u001B[0m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 109\u001B[1;33m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n\u001B[0m\u001B[0;32m 110\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_merge\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m==\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 111\u001B[0m \u001B[1;32mbreak\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_evaluate_merge\u001B[1;34m(self, not_in_cache, dataframe, feature, cube, other_cube, merge_cache)\u001B[0m\n\u001B[0;32m 94\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mor\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 95\u001B[0m \u001B[0mmerged_cube\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmerge_along_dimension\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfeature\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 96\u001B[1;33m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 97\u001B[0m \u001B[0mmerge_cache\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 98\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCLASSIFICATION\u001B[0m \u001B[1;32melse\u001B[0m\u001B[0;31m \u001B[0m\u001B[0;31m\\\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\hypercube.py\u001B[0m in \u001B[0;36mupdate\u001B[1;34m(self, dataset, predictor)\u001B[0m\n\u001B[0;32m 362\u001B[0m \u001B[0mfiltered\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfilter_dataframe\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataset\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 363\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m \u001B[1;33m>\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 364\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 365\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 366\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_diversity\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mabs\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m-\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmean\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + "2491\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2492\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2493\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2494\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2495\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2496\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2497\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2498\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2499\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2500\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2501\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2502\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2503\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2504\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2505\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2506\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2507\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2508\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n" ] } ], @@ -157,7 +222,7 @@ " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", - " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", " missed[name].append(miss)" @@ -171,10 +236,10 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "outputs": [], "source": [ - "pd.DataFrame(predicted)" + "pd.DataFrame(predicted).to_csv(\"pred.csv\")" ], "metadata": { "collapsed": false, @@ -185,10 +250,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "outputs": [], "source": [ - "pd.DataFrame(rules)" + "pd.DataFrame(rules).to_csv('rules.csv')\n", + "pd.DataFrame(missed).to_csv('missed.csv')" ], "metadata": { "collapsed": false, @@ -199,9 +265,58 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "outputs": [], - "source": [], + "source": [ + "p = pd.DataFrame(predicted)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 22, + "outputs": [ + { + "data": { + "text/plain": "492505987326.38666" + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(p.COSMiK - p.model).mean()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 23, + "outputs": [ + { + "data": { + "text/plain": " index V model GridREx \\\ncount 11664 11664.000000 11664.000000 1.166400e+04 \nmean 2016-11-01 23:30:00 454.232082 451.424130 9.016708e+09 \nmin 2016-03-04 00:00:00 263.000000 329.304591 -5.506639e+12 \n25% 2016-07-03 11:45:00 368.000000 392.160427 -5.776123e-01 \n50% 2016-11-01 23:30:00 431.000000 442.197565 -1.053650e-01 \n75% 2017-03-03 11:15:00 525.000000 507.905235 5.485643e-01 \nmax 2017-07-02 23:00:00 761.000000 639.798655 5.516910e+12 \nstd NaN 107.353337 70.294346 2.476474e+11 \n\n CART COSMiK CReEPy \ncount 11664.000000 1.166400e+04 11664.000000 \nmean 0.012740 2.897577e+11 455.121051 \nmin -0.890021 -3.547506e+13 284.408661 \n25% -0.757964 3.301165e+02 395.736008 \n50% -0.006947 4.555268e+02 448.432503 \n75% 0.810958 6.599621e+02 510.909673 \nmax 1.196391 1.997025e+14 716.249401 \nstd 0.668172 8.476597e+12 76.059168 ", + "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
indexVmodelGridRExCARTCOSMiKCReEPy
count1166411664.00000011664.0000001.166400e+0411664.0000001.166400e+0411664.000000
mean2016-11-01 23:30:00454.232082451.4241309.016708e+090.0127402.897577e+11455.121051
min2016-03-04 00:00:00263.000000329.304591-5.506639e+12-0.890021-3.547506e+13284.408661
25%2016-07-03 11:45:00368.000000392.160427-5.776123e-01-0.7579643.301165e+02395.736008
50%2016-11-01 23:30:00431.000000442.197565-1.053650e-01-0.0069474.555268e+02448.432503
75%2017-03-03 11:15:00525.000000507.9052355.485643e-010.8109586.599621e+02510.909673
max2017-07-02 23:00:00761.000000639.7986555.516910e+121.1963911.997025e+14716.249401
stdNaN107.35333770.2943462.476474e+110.6681728.476597e+1276.059168
\n
" + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p.describe()" + ], "metadata": { "collapsed": false, "pycharm": { From 636cc85951fd34c69f20b7c0a001b00a7d73871b Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:47:33 +0200 Subject: [PATCH 23/67] wip --- demo/DemoRegressionScaled.ipynb | 149 +++++++------------------------- 1 file changed, 29 insertions(+), 120 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index ddf14ec3..69677604 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -96,108 +96,35 @@ "output_type": "stream", "text": [ "2491\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2492\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2493\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2494\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2495\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2496\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2497\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2498\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2499\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2500\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2501\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2502\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2503\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2504\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2505\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2506\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2507\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2508\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n" + "GridREx\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'predict'", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mAttributeError\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/1487439369.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/808977561.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 10\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n\u001B[0;32m 11\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m---> 12\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 13\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 14\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 375\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 376\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 377\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 378\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mAttributeError\u001B[0m: 'NoneType' object has no attribute 'predict'" ] } ], "source": [ "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", "\n", + "extractors = ['GridEx', 'GridREx', 'CART', 'COSMiK', 'CReEPy']\n", + "\n", "TESTB = [i for i in range(2491, 2509)]\n", "\n", - "predicted = {'index': [], 'V': [], 'model': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "predicted = {name: [] for name in ['index', 'V', 'model'] + extractors}\n", "\n", - "rules = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "rules = {name: [] for name in ['BR'] + extractors}\n", "\n", - "missed = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "missed = {name: [] for name in ['BR'] + extractors}\n", "\n", "for testB in TESTB:\n", " rules['BR'].append(testB)\n", @@ -218,14 +145,15 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(['GridREx', 'CART', 'COSMiK', 'CReEPy'], [gridrex, cart, cosmik, creepy]):\n", + " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)" + " missed[name].append(miss)\n", + " break" ], "metadata": { "collapsed": false, @@ -236,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "outputs": [], "source": [ "pd.DataFrame(predicted).to_csv(\"pred.csv\")" @@ -250,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "outputs": [], "source": [ "pd.DataFrame(rules).to_csv('rules.csv')\n", @@ -265,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "outputs": [], "source": [ "p = pd.DataFrame(predicted)" @@ -279,17 +207,8 @@ }, { "cell_type": "code", - "execution_count": 22, - "outputs": [ - { - "data": { - "text/plain": "492505987326.38666" - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "abs(p.COSMiK - p.model).mean()" ], @@ -302,18 +221,8 @@ }, { "cell_type": "code", - "execution_count": 23, - "outputs": [ - { - "data": { - "text/plain": " index V model GridREx \\\ncount 11664 11664.000000 11664.000000 1.166400e+04 \nmean 2016-11-01 23:30:00 454.232082 451.424130 9.016708e+09 \nmin 2016-03-04 00:00:00 263.000000 329.304591 -5.506639e+12 \n25% 2016-07-03 11:45:00 368.000000 392.160427 -5.776123e-01 \n50% 2016-11-01 23:30:00 431.000000 442.197565 -1.053650e-01 \n75% 2017-03-03 11:15:00 525.000000 507.905235 5.485643e-01 \nmax 2017-07-02 23:00:00 761.000000 639.798655 5.516910e+12 \nstd NaN 107.353337 70.294346 2.476474e+11 \n\n CART COSMiK CReEPy \ncount 11664.000000 1.166400e+04 11664.000000 \nmean 0.012740 2.897577e+11 455.121051 \nmin -0.890021 -3.547506e+13 284.408661 \n25% -0.757964 3.301165e+02 395.736008 \n50% -0.006947 4.555268e+02 448.432503 \n75% 0.810958 6.599621e+02 510.909673 \nmax 1.196391 1.997025e+14 716.249401 \nstd 0.668172 8.476597e+12 76.059168 ", - "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
indexVmodelGridRExCARTCOSMiKCReEPy
count1166411664.00000011664.0000001.166400e+0411664.0000001.166400e+0411664.000000
mean2016-11-01 23:30:00454.232082451.4241309.016708e+090.0127402.897577e+11455.121051
min2016-03-04 00:00:00263.000000329.304591-5.506639e+12-0.890021-3.547506e+13284.408661
25%2016-07-03 11:45:00368.000000392.160427-5.776123e-01-0.7579643.301165e+02395.736008
50%2016-11-01 23:30:00431.000000442.197565-1.053650e-01-0.0069474.555268e+02448.432503
75%2017-03-03 11:15:00525.000000507.9052355.485643e-010.8109586.599621e+02510.909673
max2017-07-02 23:00:00761.000000639.7986555.516910e+121.1963911.997025e+14716.249401
stdNaN107.35333770.2943462.476474e+110.6681728.476597e+1276.059168
\n
" - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "p.describe()" ], From 55cf276a432bc2479abcc4a993288274e7ae1abd Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:29:40 +0200 Subject: [PATCH 24/67] wip --- demo/DemoRegressionScaled.ipynb | 154 +++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 23 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 69677604..3c44844c 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -49,32 +49,32 @@ "execution_count": 3, "outputs": [], "source": [ - "def gridex(model, train, test, normalization, s, m):\n", + "def gridex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridEx = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3), (0.75, 4)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridEx.extract(train)\n", " return gridEx.brute_predict(test), gridEx.n_rules, sum([p is None for p in gridEx.predict(test)])\n", " \n", - "def gridrex(model, train, test, normalization, s, m):\n", + "def gridrex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", - "def cart(model, train, test, normalization, s, m):\n", + "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", " CART.extract(train)\n", - " return CART.predict(test) * s + m, CART.n_rules, sum([p is None for p in CART.predict(test)])\n", + " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", - "def cosmik(model, train, test, normalization, s, m):\n", + "def cosmik(model, train, test, normalization):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", " output=Target.CONSTANT, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization, s, m):\n", + "def creepy(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", @@ -95,21 +95,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2491\n", - "GridREx\n" - ] - }, - { - "ename": "AttributeError", - "evalue": "'NoneType' object has no attribute 'predict'", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mAttributeError\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/1487439369.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/808977561.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 10\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n\u001B[0;32m 11\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m---> 12\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 13\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 14\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 375\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 376\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 377\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 378\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mAttributeError\u001B[0m: 'NoneType' object has no attribute 'predict'" + "2491\n" ] } ], @@ -144,12 +130,12 @@ " model = KNN(200, weights='distance', p=1).fit(scaledTrain.iloc[:, :-1], scaledTrain.iloc[:, -1])\n", " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", - "\n", + " break\n", " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", - " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", " missed[name].append(miss)\n", @@ -162,6 +148,128 @@ } } }, + { + "cell_type": "code", + "execution_count": 13, + "outputs": [ + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_11724/3287760623.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mCART\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcart\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 2\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 3\u001B[1;33m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, dataframe, mapping)\u001B[0m\n\u001B[0;32m 57\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;32mreturn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0ma\u001B[0m \u001B[0mlist\u001B[0m \u001B[0mof\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 58\u001B[0m \"\"\"\n\u001B[1;32m---> 59\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__convert\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 60\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + ] + } + ], + "source": [ + "CART = Extractor.cart(model, normalization=normalization)\n", + "CART.extract(scaledTrain)\n", + "p = CART.predict(scaledTest)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [ + { + "data": { + "text/plain": "(648, 648)" + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.array(p).shape" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [ + { + "data": { + "text/plain": "(648,)" + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.predict(scaledTest).shape" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "data": { + "text/plain": " index V model GridEx GridREx \\\n0 2016-03-04 00:00:00 410.0 518.750997 0.0 -0.381030 \n1 2016-03-04 01:00:00 400.0 522.352090 0.0 -0.407892 \n2 2016-03-04 02:00:00 395.0 525.129143 0.0 -0.433331 \n3 2016-03-04 03:00:00 408.0 528.767307 0.0 -0.461158 \n4 2016-03-04 04:00:00 406.0 528.091871 0.0 -0.477063 \n.. ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 0.0 -0.294504 \n644 2016-03-30 20:00:00 501.0 489.614882 0.0 -0.278289 \n645 2016-03-30 21:00:00 518.0 490.609540 0.0 -0.267642 \n646 2016-03-30 22:00:00 510.0 491.321955 0.0 -0.251590 \n647 2016-03-30 23:00:00 511.0 491.970557 0.0 -0.229831 \n\n CART \\\n0 [441.86581920903956, 441.86581920903956, 441.8... \n1 [441.86581920903956, 441.86581920903956, 441.8... \n2 [441.86581920903956, 441.86581920903956, 441.8... \n3 [441.86581920903956, 441.86581920903956, 441.8... \n4 [441.86581920903956, 441.86581920903956, 441.8... \n.. ... \n643 [441.86581920903956, 441.86581920903956, 441.8... \n644 [441.86581920903956, 441.86581920903956, 441.8... \n645 [441.86581920903956, 441.86581920903956, 441.8... \n646 [441.86581920903956, 441.86581920903956, 441.8... \n647 [441.86581920903956, 441.86581920903956, 441.8... \n\n COSMiK \\\n0 [340.3877551020408, 340.3877551020408, 340.387... \n1 [340.3877551020408, 340.3877551020408, 340.387... \n2 [340.3877551020408, 340.3877551020408, 340.387... \n3 [340.3877551020408, 340.3877551020408, 340.387... \n4 [340.3877551020408, 340.3877551020408, 340.387... \n.. ... \n643 [340.3877551020408, 340.3877551020408, 340.387... \n644 [340.3877551020408, 340.3877551020408, 340.387... \n645 [340.3877551020408, 340.3877551020408, 340.387... \n646 [340.3877551020408, 340.3877551020408, 340.387... \n647 [340.3877551020408, 340.3877551020408, 340.387... \n\n CReEPy \n0 [464.19181456754364, 462.0928005366921, 459.64... \n1 [464.19181456754364, 462.0928005366921, 459.64... \n2 [464.19181456754364, 462.0928005366921, 459.64... \n3 [464.19181456754364, 462.0928005366921, 459.64... \n4 [464.19181456754364, 462.0928005366921, 459.64... \n.. ... \n643 [464.19181456754364, 462.0928005366921, 459.64... \n644 [464.19181456754364, 462.0928005366921, 459.64... \n645 [464.19181456754364, 462.0928005366921, 459.64... \n646 [464.19181456754364, 462.0928005366921, 459.64... \n647 [464.19181456754364, 462.0928005366921, 459.64... \n\n[648 rows x 8 columns]", + "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.7509970.0-0.381030[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
12016-03-04 01:00:00400.0522.3520900.0-0.407892[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
22016-03-04 02:00:00395.0525.1291430.0-0.433331[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
32016-03-04 03:00:00408.0528.7673070.0-0.461158[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
42016-03-04 04:00:00406.0528.0918710.0-0.477063[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
...........................
6432016-03-30 19:00:00497.0487.5210850.0-0.294504[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6442016-03-30 20:00:00501.0489.6148820.0-0.278289[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6452016-03-30 21:00:00518.0490.6095400.0-0.267642[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6462016-03-30 22:00:00510.0491.3219550.0-0.251590[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6472016-03-30 23:00:00511.0491.9705570.0-0.229831[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
\n

648 rows × 8 columns

\n
" + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.DataFrame(predicted)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "sdsfgd" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": null, From 68f6495da41761c0f813ede1436a818f25a712ab Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:37:00 +0200 Subject: [PATCH 25/67] wip --- demo/DemoRegressionScaled.ipynb | 43 +++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 3c44844c..bcf9ca55 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -150,7 +150,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 19, "outputs": [ { "ename": "KeyboardInterrupt", @@ -159,10 +159,11 @@ "traceback": [ "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_11724/3287760623.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mCART\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcart\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 2\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 3\u001B[1;33m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, dataframe, mapping)\u001B[0m\n\u001B[0;32m 57\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;32mreturn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0ma\u001B[0m \u001B[0mlist\u001B[0m \u001B[0mof\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 58\u001B[0m \"\"\"\n\u001B[1;32m---> 59\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__convert\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 60\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_7300/1366764375.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mranked\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mFeatureRanker\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mrankings\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 2\u001B[1;33m CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n\u001B[0m\u001B[0;32m 3\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[0;32m 4\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mgridex\u001B[1;34m(predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 298\u001B[0m \"\"\"\n\u001B[0;32m 299\u001B[0m \u001B[1;32mfrom\u001B[0m \u001B[0mpsyke\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextraction\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mhypercubic\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgridex\u001B[0m \u001B[1;32mimport\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 300\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthreshold\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mseed\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 301\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 302\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 18\u001B[0m def __init__(self, predictor, grid: Grid, min_examples: int, threshold: float, normalization=None,\n\u001B[0;32m 19\u001B[0m seed=get_default_random_seed()):\n\u001B[1;32m---> 20\u001B[1;33m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCONSTANT\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 21\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgrid\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 22\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmin_examples\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", @@ -175,7 +176,9 @@ } ], "source": [ - "CART = Extractor.cart(model, normalization=normalization)\n", + "ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", + "CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n", + " threshold=5, min_examples=1, normalization=normalization)\n", "CART.extract(scaledTrain)\n", "p = CART.predict(scaledTest)" ], @@ -188,13 +191,27 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 17, + "outputs": [], + "source": [ + "CART.normalization" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 15, "outputs": [ { "data": { - "text/plain": "(648, 648)" + "text/plain": "(648,)" }, - "execution_count": 9, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -211,19 +228,19 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 16, "outputs": [ { "data": { - "text/plain": "(648,)" + "text/plain": "array([ 5.94787596e-01, 6.27852580e-01, 6.53351288e-01, 6.86756656e-01,\n 6.80554845e-01, 6.56268596e-01, 6.41509728e-01, 6.23215360e-01,\n 5.06968206e-01, 4.49547691e-01, 4.50112948e-01, 3.89409986e-01,\n 4.17987119e-01, 4.47363530e-01, 4.73589506e-01, 5.27920839e-01,\n 5.65069947e-01, 6.30275078e-01, 6.50014692e-01, 6.25667786e-01,\n 5.32084121e-01, 5.11315913e-01, 4.66818875e-01, 3.79488214e-01,\n 3.49910991e-01, 2.21345403e-01, 1.93290286e-01, 1.29067767e-01,\n 1.29278790e-01, 9.70285114e-02, 1.16471507e-01, 1.20058101e-01,\n 1.10625154e-01, 9.43989669e-02, 4.62561519e-02, 1.54927422e-02,\n -3.67243793e-02, -9.73570606e-02, -8.82136957e-02, -8.59049356e-02,\n -1.88867848e-01, -1.98258070e-01, -2.14678123e-01, -2.25880942e-01,\n -2.73753227e-01, -2.86873033e-01, -3.36773813e-01, -3.41153580e-01,\n -2.72939657e-01, -2.60564106e-01, -2.22866189e-01, -3.16103250e-01,\n -4.57077669e-01, -5.69617624e-01, -4.85024701e-01, -4.38443662e-01,\n -1.81425306e-01, 3.16564715e-02, 2.01502725e-01, 3.30505305e-01,\n 4.34886887e-01, 4.88884946e-01, 4.81334210e-01, 4.72318295e-01,\n 4.49382717e-01, 3.71368779e-01, 3.10463488e-01, 2.27584909e-01,\n 1.96014710e-01, 1.21620524e-01, 8.96840584e-02, 9.66028209e-02,\n 5.56857290e-02, 9.76252433e-02, 1.16906912e-01, 9.32035580e-02,\n 1.61667957e-01, 2.43306894e-01, 4.00764438e-01, 4.08452018e-01,\n 5.04550610e-01, 6.33769305e-01, 7.70897913e-01, 8.66805211e-01,\n 9.64611914e-01, 9.24679976e-01, 9.83779612e-01, 9.57014376e-01,\n 1.18224046e+00, 1.18196180e+00, 1.25001824e+00, 1.26741209e+00,\n 1.31358148e+00, 1.30732589e+00, 1.27459292e+00, 1.28298857e+00,\n 1.30996624e+00, 1.33409461e+00, 1.34064098e+00, 1.33456069e+00,\n 1.26926162e+00, 1.23751719e+00, 1.21728041e+00, 1.18781088e+00,\n 1.07904111e+00, 9.78348868e-01, 8.94449891e-01, 8.02642930e-01,\n 7.67312190e-01, 7.79881638e-01, 7.56459980e-01, 6.39844731e-01,\n 5.59816075e-01, 5.07320578e-01, 3.96137907e-01, 3.08059300e-01,\n 3.49538875e-01, 4.37822132e-01, 5.19863709e-01, 4.83896648e-01,\n 5.49159005e-01, 6.51009546e-01, 6.66199773e-01, 6.75533692e-01,\n 7.14284944e-01, 7.49667837e-01, 7.27343955e-01, 7.50315379e-01,\n 7.35121200e-01, 7.11411018e-01, 6.50371738e-01, 6.57852210e-01,\n 6.88303646e-01, 6.88771323e-01, 6.53846882e-01, 6.39351067e-01,\n 6.42534572e-01, 6.11897413e-01, 5.48695256e-01, 5.36093577e-01,\n 4.68813273e-01, 4.81517348e-01, 4.16924579e-01, 2.27269556e-01,\n 6.95082620e-02, 4.73304329e-02, 4.31446914e-02, 1.51598952e-03,\n -1.63872715e-01, -2.66752323e-01, -3.04611850e-01, -3.09940192e-01,\n -3.80448778e-01, -5.20578055e-01, -5.58929636e-01, -5.66185437e-01,\n -5.50515452e-01, -4.97476048e-01, -4.69612472e-01, -4.37668499e-01,\n -4.08758754e-01, -4.70707072e-01, -5.20063597e-01, -5.77177092e-01,\n -6.33710168e-01, -6.80554694e-01, -7.54318656e-01, -7.98758101e-01,\n -8.44280781e-01, -8.98363509e-01, -8.95358460e-01, -8.86503609e-01,\n -8.85599683e-01, -8.82890386e-01, -8.53614367e-01, -8.31532738e-01,\n -8.33123651e-01, -6.27513729e-01, -1.37548268e-01, 1.58522033e-01,\n 3.43769358e-01, 3.53969327e-01, 3.30450997e-01, 3.16298042e-01,\n 3.07723557e-01, 2.50430376e-01, 1.79257019e-01, 1.48908911e-01,\n 1.94124475e-01, 2.96394183e-01, 3.65626353e-01, 4.03952940e-01,\n 4.89904130e-01, 5.43864115e-01, 5.70022845e-01, 5.78188313e-01,\n 6.02378943e-01, 5.53382870e-01, 5.76370663e-01, 4.53366033e-01,\n 5.46894816e-01, 6.22814657e-01, 6.98452824e-01, 8.43786807e-01,\n 9.03241794e-01, 9.80655929e-01, 1.00069167e+00, 1.03552441e+00,\n 1.05781862e+00, 1.03407151e+00, 1.01060741e+00, 9.95349018e-01,\n 9.83748339e-01, 9.77971820e-01, 9.59232606e-01, 9.75587590e-01,\n 9.86886799e-01, 9.89016229e-01, 1.01371718e+00, 1.02127174e+00,\n 1.01666168e+00, 1.06380858e+00, 1.04014026e+00, 9.68632461e-01,\n 8.50561688e-01, 7.88313619e-01, 8.60332530e-01, 8.39510615e-01,\n 8.19082407e-01, 8.41616540e-01, 8.45556255e-01, 8.78204007e-01,\n 9.01526215e-01, 8.99408318e-01, 9.75302328e-01, 1.07239590e+00,\n 1.12769248e+00, 1.10865204e+00, 1.16262617e+00, 1.19097896e+00,\n 1.18783719e+00, 1.13230601e+00, 1.14472708e+00, 1.16748934e+00,\n 1.15556360e+00, 1.15497319e+00, 1.14661413e+00, 1.12722389e+00,\n 1.11759420e+00, 1.11397318e+00, 1.12588339e+00, 1.10233146e+00,\n 1.06748597e+00, 9.94411455e-01, 9.34246345e-01, 9.26761867e-01,\n 8.79439448e-01, 8.34147761e-01, 7.88468961e-01, 2.63101924e-01,\n 2.58441893e-01, 2.75837911e-01, 3.73833340e-01, 4.06661808e-01,\n 4.50858947e-01, 4.25019531e-01, 3.93777296e-01, 4.25283768e-01,\n 4.50114669e-01, 4.32118982e-01, 4.26723712e-01, 4.25086302e-01,\n 4.12253850e-01, 4.20772144e-01, 3.73951452e-01, 3.84730496e-01,\n 4.15677813e-01, 4.70782896e-01, 5.18673694e-01, 6.28786146e-01,\n 7.11168389e-01, 8.38840673e-01, 9.24391393e-01, 1.00049266e+00,\n 1.02386999e+00, 1.07021416e+00, 1.09681443e+00, 1.14425776e+00,\n 1.35203664e+00, 1.36045510e+00, 1.41445129e+00, 1.42926777e+00,\n 1.48989705e+00, 1.48908458e+00, 1.52517735e+00, 1.55774308e+00,\n 1.53297116e+00, 1.48283249e+00, 1.45336878e+00, 1.43025635e+00,\n 1.42162708e+00, 1.39982454e+00, 1.38697364e+00, 1.40470142e+00,\n 1.41465673e+00, 1.41616546e+00, 1.39234617e+00, 1.39974420e+00,\n 1.37379750e+00, 1.31108897e+00, 1.29610318e+00, 1.26624619e+00,\n 1.21507803e+00, 1.18620871e+00, 1.16431475e+00, 1.08350728e+00,\n 1.04351910e+00, 8.14857616e-01, 9.19351580e-01, 9.92161799e-01,\n 1.05183656e+00, 1.06302057e+00, 1.15394942e+00, 1.26940951e+00,\n 1.28522890e+00, 1.44338788e+00, 1.36122698e+00, 1.22730775e+00,\n 1.26271454e+00, 1.25683022e+00, 1.26641669e+00, 1.35523754e+00,\n 1.35363264e+00, 1.19374746e+00, 8.84222340e-01, 8.45086765e-01,\n 8.26652842e-01, 7.78535822e-01, 7.91430635e-01, 7.51209328e-01,\n 7.16570137e-01, 7.12982315e-01, 6.71535968e-01, 6.59427982e-01,\n 6.61309295e-01, 6.00194599e-01, 5.46063131e-01, 5.06583233e-01,\n 4.60146040e-01, 4.13683133e-01, 3.27972661e-01, 2.84937524e-01,\n 2.45562643e-01, 2.77127619e-01, 2.53966681e-01, 2.33893354e-01,\n 2.36568447e-01, 2.28037916e-01, 2.49038569e-01, 2.64400726e-01,\n 2.94056569e-01, 2.90707304e-01, 2.83857999e-01, 3.01860592e-01,\n 3.14841571e-01, 3.39277627e-01, 3.01466072e-01, 2.93500775e-01,\n 2.87430279e-01, 2.70941481e-01, 3.06142458e-01, 3.36453506e-01,\n 3.49177875e-01, 3.60445946e-01, 3.73710668e-01, 4.04441898e-01,\n 3.94960073e-01, 3.99827257e-01, 3.77291659e-01, 3.30380384e-01,\n 3.37732723e-01, 3.47186442e-01, 3.52084474e-01, 3.77269993e-01,\n 3.77988341e-01, 3.80380920e-01, 3.69822861e-01, 3.58249184e-01,\n 3.64697520e-01, 3.33135345e-01, 3.09091741e-01, 3.80991398e-01,\n 3.61867705e-01, 3.19234148e-01, 3.21624558e-01, 2.17452675e-01,\n 1.92589914e-01, 1.79242718e-01, 1.53009476e-01, 9.77992145e-02,\n 3.23604394e-02, -2.94612149e-02, -1.14045668e-01, -1.55128381e-01,\n -1.57616220e-01, -1.71918305e-01, -1.50894217e-01, -1.67564490e-01,\n -1.88622273e-01, -2.07652581e-01, -2.58115218e-01, -2.93005352e-01,\n -3.38270047e-01, -1.49019196e-01, -3.44280222e-01, -3.28079666e-01,\n -3.26221855e-01, -2.34694433e-01, -2.19842775e-01, -2.43728538e-01,\n -2.38287675e-01, -2.07637015e-01, -2.99053400e-01, -3.51944773e-01,\n -2.98335213e-01, -2.75216185e-01, -2.52291688e-01, -5.92295676e-01,\n -6.29698629e-01, -6.37034248e-01, -6.67193507e-01, -6.61285775e-01,\n -6.59767960e-01, -6.55582862e-01, -6.60398712e-01, -6.48089994e-01,\n -6.30606033e-01, -6.13985068e-01, -5.94299126e-01, -5.51793470e-01,\n -5.21829072e-01, -5.33152444e-01, -5.58533647e-01, -5.30664797e-01,\n -4.96012827e-01, -4.81793417e-01, -4.94722300e-01, -5.05906482e-01,\n -5.84771524e-01, -6.21891793e-01, -6.57652629e-01, -6.74505274e-01,\n -6.77925168e-01, -6.63411854e-01, -6.70761225e-01, -6.71313501e-01,\n -6.78285438e-01, -6.88366243e-01, -6.75084854e-01, -6.86645949e-01,\n -6.86878877e-01, -7.03152004e-01, -7.11550530e-01, -7.06169968e-01,\n -7.06030613e-01, -7.11075855e-01, -6.80750629e-01, -6.72296413e-01,\n -6.65302849e-01, -6.74474185e-01, -6.70617066e-01, -6.70406918e-01,\n -6.31489293e-01, -5.41195959e-01, -5.06100680e-01, -4.73968482e-01,\n -4.80026300e-01, -4.66280906e-01, -4.94750733e-01, -4.66159421e-01,\n -4.53704671e-01, -4.65426691e-01, -4.67830842e-01, -4.48499002e-01,\n -4.37999267e-01, -4.06635710e-01, -4.36565611e-01, -4.87644475e-01,\n -5.10015971e-01, -5.26553795e-01, -5.24534882e-01, -5.17204913e-01,\n -5.18734726e-01, -4.88314619e-01, -4.81448198e-01, -4.49654115e-01,\n -3.59544883e-01, -2.89470852e-01, -2.52844742e-01, -1.92796426e-01,\n 3.61641165e-02, 2.67556715e-01, 2.88468372e-01, 2.69765539e-01,\n 3.03003617e-01, 1.56430282e-01, 8.94392059e-02, 8.35489348e-02,\n 9.53608325e-02, 1.06359033e-01, 4.85854783e-02, 5.95200015e-02,\n 8.92134085e-02, 1.71655377e-01, -1.62529760e-01, -9.18029145e-02,\n -7.00795871e-03, 3.55558744e-02, 7.13713389e-02, 8.51286206e-02,\n 8.85206272e-02, 9.03776601e-02, 7.80484532e-02, 7.76571930e-02,\n 7.06369278e-02, 5.61746134e-02, 7.89197268e-02, 8.51250688e-02,\n 1.11657860e-01, 8.77699810e-02, 1.40152096e-01, 1.41833281e-01,\n 1.40840041e-01, 1.85234816e-01, 1.53057074e-01, 1.61289830e-01,\n 1.60056307e-01, 1.47913238e-01, 1.59117011e-01, 1.24454678e-01,\n 8.40377871e-02, 9.25309859e-02, 8.57329210e-02, 7.57535730e-02,\n 6.16577201e-02, 1.06938161e-01, 1.02192008e-01, 1.71101839e-01,\n 2.19090649e-01, 1.94452383e-01, 1.52915395e-01, 1.42999064e-01,\n 2.34941176e-02, -1.69292889e-02, -4.84760026e-02, -8.84035972e-02,\n -1.06477491e-01, -1.46097303e-01, -1.68977796e-01, -1.69546328e-01,\n -3.06123179e-01, -2.86830814e-01, -3.27339805e-01, -3.04824309e-01,\n -3.08424522e-01, -3.13009099e-01, -3.08204615e-01, -3.30643627e-01,\n -3.47108546e-01, -3.71663036e-01, -3.69800392e-01, -3.81838621e-01,\n -4.11920074e-01, -4.26296448e-01, -4.33323503e-01, -4.32084854e-01,\n -4.26418566e-01, -3.99080526e-01, -3.43601617e-01, -3.32431572e-01,\n -3.17056043e-01, -2.77115852e-01, -2.52202840e-01, -2.14129148e-01,\n -2.00518462e-01, -1.46597547e-01, 2.36017486e-01, 2.72685842e-01,\n 3.48128210e-01, 2.84062462e-01, 2.81911796e-01, 2.71269801e-01,\n 2.17936171e-01, 1.81334194e-01, 1.55982915e-01, 1.36845118e-01,\n 8.53148103e-02, 5.47081435e-02, -1.14402861e-02, -5.94431312e-02,\n -9.09789281e-02, -1.44687840e-01, -1.67931366e-01, -1.76196230e-01,\n -1.84964598e-01, -1.80156999e-01, -1.82591858e-01, -2.04167736e-01,\n -2.17966566e-01, -1.25904259e-01, -1.53099147e-01, -1.62306106e-01,\n -2.16528907e-01, -2.69388887e-01, -3.25151001e-01, -5.16529016e-02,\n 2.34635280e-01, 2.86861522e-01, 2.49274427e-01, 1.65100897e-01,\n 1.31739752e-01, 1.38150405e-01, 1.26449068e-01, 1.62529445e-01,\n 1.71290348e-01, 1.77941660e-01, 1.68343103e-01, 1.21794429e-01,\n 7.66499527e-02, 9.79244195e-02, 1.13147159e-01, 1.19400540e-01,\n 1.31218961e-01, 1.32184095e-01, 1.21069556e-01, 1.14229094e-01,\n 1.29832401e-01, 1.24523142e-01, 1.39992356e-01, 1.42168147e-01,\n 2.48498740e-01, 2.74234518e-01, 3.09358436e-01, 3.08036727e-01,\n 3.27261828e-01, 3.36394705e-01, 3.42936055e-01, 3.48891465e-01])" }, - "execution_count": 11, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "model.predict(scaledTest).shape" + "model.predict(scaledTest)" ], "metadata": { "collapsed": false, From 8b22d9234db9c9b501bb5d5678f395890d8c5283 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 00:16:08 +0200 Subject: [PATCH 26/67] wip --- demo/DemoRegression.ipynb | 2010 ------------------------------- demo/DemoRegressionScaled.ipynb | 325 +++-- 2 files changed, 204 insertions(+), 2131 deletions(-) delete mode 100644 demo/DemoRegression.ipynb diff --git a/demo/DemoRegression.ipynb b/demo/DemoRegression.ipynb deleted file mode 100644 index 3bef7b2a..00000000 --- a/demo/DemoRegression.ipynb +++ /dev/null @@ -1,2010 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "f52126f3", - "metadata": {}, - "source": [ - "# PSyKE's demo for regression tasks\n", - "\n", - "Some imports." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "6b710e7c", - "metadata": {}, - "outputs": [], - "source": [ - "from psyke import Extractor, Clustering, EvaluableModel\n", - "from psyke.tuning.pedro import PEDRO\n", - "from psyke.tuning import Objective\n", - "from psyke.tuning.crash import CRASH\n", - "from sklearn.tree import DecisionTreeRegressor\n", - "from psyke.utils.logic import pretty_theory\n", - "from psyke.utils.metrics import mae, mse, r2\n", - "from sklearn.model_selection import train_test_split\n", - "from psyke.utils import Target\n", - "import pandas as pd" - ] - }, - { - "cell_type": "markdown", - "id": "d7c90ed2", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "Import a dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = pd.read_csv(\"../test/resources/datasets/df.csv\")\n", - "dataset = dataset[[\"X\", \"Y\", \"Z0\"]].dropna()\n", - "#dataset = pd.read_csv(\"../test/resources/datasets/CCPP.csv\", sep=\";\", decimal=\",\")\n", - "#dataset" - ] - }, - { - "cell_type": "markdown", - "id": "d673b766", - "metadata": {}, - "source": [ - "Split between train and test set in a reproducible way." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "03fc5e2c", - "metadata": {}, - "outputs": [], - "source": [ - "train, test = train_test_split(dataset, test_size=0.85, random_state=10)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "outputs": [], - "source": [ - "#from psyke.tuning.orchid import OrCHiD\n", - "\n", - "#orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.CREAM, output=Target.REGRESSION,\n", - "# max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", - "#orchid.search()\n", - "#(_, _, depth, threshold) = orchid.get_best()[0]" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "id": "fa6754a0", - "metadata": {}, - "source": [ - "We use as predictor a KNN and we train it." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "bed764ca", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MAE = 0.00\n", - "MSE = 0.00\n", - "R2 = 1.00\n" - ] - } - ], - "source": [ - "#predictor = KNeighborsRegressor(n_neighbors=3).fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "predictor = DecisionTreeRegressor().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "#predictor = LinearRegression().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "\n", - "predicted = predictor.predict(test.iloc[:, :-1]).flatten()\n", - "true = test.iloc[:, -1]\n", - "\n", - "print(f'MAE = {mae(true, predicted):.2f}')\n", - "print(f'MSE = {mse(true, predicted):.2f}')\n", - "print(f'R2 = {r2(true, predicted):.2f}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "We define a function to print the extractors' evaluation" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def print_scores(scores):\n", - " print(f'MAE = {scores[EvaluableModel.RegressionScore.MAE][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.RegressionScore.MAE][1]:.2f} (BB)\\n'\n", - " f'MSE = {scores[EvaluableModel.RegressionScore.MSE][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.RegressionScore.MSE][1]:.2f} (BB)\\n'\n", - " f'R2 = {scores[EvaluableModel.RegressionScore.R2][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.RegressionScore.R2][1]:.2f} (BB)')\n", - "\n", - "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, task=EvaluableModel.Task.REGRESSION,\n", - " scoring_function=[EvaluableModel.RegressionScore.MAE, EvaluableModel.RegressionScore.MSE,\n", - " EvaluableModel.RegressionScore.R2])" - ] - }, - { - "cell_type": "markdown", - "id": "96835867", - "metadata": {}, - "source": [ - "We create several extractors that use ITER, GridEx and GridREx algorithms to extract prolog rules from the predictor." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "COSMiK performance (4 rules with 87.83% coverage):\n", - "MAE = 0.00 (data), 0.00 (BB)\n", - "MSE = 0.00 (data), 0.00 (BB)\n", - "R2 = 1.00 (data), 1.00 (BB)\n", - "\n", - "COSMiK extracted rules:\n", - "\n", - "'Z0'(X, Y, 1.0) :-\n", - " X in [0.0, 0.48], Y in [0.0, 0.48].\n", - "'Z0'(X, Y, -1.0) :-\n", - " X in [0.65, 1.0], Y in [0.0, 0.44].\n", - "'Z0'(X, Y, -1.0) :-\n", - " X in [0.90, 1.0], Y in [0.75, 1.0].\n", - "'Z0'(X, Y, 0.0) :-\n", - " X in [0.05, 0.83], Y in [0.57, 0.99].\n" - ] - } - ], - "source": [ - "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True)\n", - "theory_from_cosmik = cosmik.extract(train)\n", - "scores, completeness = get_scores(cosmik, test, predictor)\n", - "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "COSMiK performance (4 rules with 89.92% coverage):\n", - "MAE = 0.08 (data), 0.08 (BB)\n", - "MSE = 0.03 (data), 0.03 (BB)\n", - "R2 = 0.96 (data), 0.96 (BB)\n", - "\n", - "COSMiK extracted rules:\n", - "\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.0, 0.48], Y in [0.0, 0.48], Z0 is 1.0.\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.65, 1.0], Y in [0.0, 0.44], Z0 is -1.0.\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.94, 1.0], Y in [0.79, 1.0], Z0 is -1.0.\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.05, 0.98], Y in [0.57, 0.99], Z0 is 0.53 - 0.82 * X - 0.28 * Y.\n" - ] - } - ], - "source": [ - "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True, output=Target.REGRESSION)\n", - "theory_from_cosmik = cosmik.extract(train)\n", - "scores, completeness = get_scores(cosmik, test, predictor)\n", - "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 10, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", - "\n", - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 2.75, 5 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.99, 5)])\n", - "\n", - "**********************\n", - "Best Predictive loss\n", - "**********************\n", - "MAE = 2.23, 9 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (3)\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 4.06, 2 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.99, 2)])\n", - "\n", - "GridEx performance (5 rules with 100.00% coverage):\n", - "MAE = 2.79 (data), 2.79 (BB)\n", - "MSE = 14.54 (data), 14.52 (BB)\n", - "R2 = 0.42 (data), 0.42 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "'Z4'(X, Y, 3.99) :-\n", - " Y in [-0.00, 0.19].\n", - "'Z4'(X, Y, 6.53) :-\n", - " Y in [0.19, 0.4].\n", - "'Z4'(X, Y, 9.37) :-\n", - " Y in [0.4, 0.6].\n", - "'Z4'(X, Y, 11.42) :-\n", - " Y in [0.6, 0.80].\n", - "'Z4'(X, Y, 2.75) :-\n", - " Y in [0.80, 1.00].\n" - ] - } - ], - "source": [ - "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL)\n", - "pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridEx = Extractor.gridex(predictor, grid, threshold=threshold)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ORBIt performance (2 rules with 100.00% coverage):\n", - "MAE = 4.15 (data), 4.15 (BB)\n", - "MSE = 28.12 (data), 28.10 (BB)\n", - "R2 = -0.13 (data), -0.13 (BB)\n", - "\n", - "ORBIt extracted rules:\n", - "\n", - "'Z4'(X, Y, 15.42) :-\n", - " 0.02 * X - 0.03 * Y =< -0.01, -0.02 * X + 0.03 * Y =< 0.01, 1.0 * X + 0.0 * Y =< 0.46, -1.0 * X + 0.0 * Y =< -0.16.\n", - "'Z4'(X, Y, 5.053691).\n" - ] - } - ], - "source": [ - "orbit = Extractor.orbit(predictor, depth=3, error_threshold=1.0, gauss_components=2, output=Target.REGRESSION)\n", - "theory_from_orbit = orbit.extract(train)\n", - "scores, completeness = get_scores(orbit, test, predictor)\n", - "print(f'ORBIt performance ({orbit.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nORBIt extracted rules:\\n\\n' + pretty_theory(theory_from_orbit))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 14, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules with 100.00% coverage):\n", - "MAE = 0.68 (data), 0.71 (BB)\n", - "MSE = 2.05 (data), 2.06 (BB)\n", - "R2 = 0.92 (data), 0.92 (BB)\n", - "\n", - "CReEPy extracted rules (ExACT):\n", - "\n", - "'Z4'(X, Y, Z4) :-\n", - " X in [0.15, 0.84], Y in [-0.00, 0.59], Z4 is 7.49 - 7.63 * X + 12.05 * Y.\n", - "'Z4'(X, Y, Z4) :-\n", - " X in [0.15, 0.84], Y in [-0.00, 0.84], Z4 is 9.0 - 12.0 * X + 15.0 * Y.\n", - "'Z4'(X, Y, Z4) :-\n", - " Y in [-0.00, 1.00], Z4 is 2.0 + 4.0 * X - 3.0 * Y.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=3, error_threshold=0.02, output=Target.REGRESSION,\n", - " clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules (ExACT):\\n\\n' + pretty_theory(theory_from_creepy))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (4 rules with 100.00% coverage):\n", - "MAE = 3.37 (data), 3.47 (BB)\n", - "MSE = 18.62 (data), 19.19 (BB)\n", - "R2 = 0.94 (data), 0.93 (BB)\n", - "\n", - "CReEPy extracted rules (CREAM):\n", - "\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 32.45], V in [34.02, 50.16], AP in [997.90, 1026.41], RH in [35.63, 100.10], PE is 502.53 - 2.16 * AP - 0.26 * AT + 0.01 * RH - 0.11 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 35.77], V in [25.35, 81.56], AP in [997.84, 1026.45], RH in [25.55, 100.12], PE is 234.73 - 1.42 * AP - 0.29 * AT + 0.26 * RH - 0.12 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [3.30, 14.60], V in [34.68, 44.47], AP in [1011.31, 1033.25], RH in [58.98, 98.68], PE is 720.26 - 2.20 * AP - 0.47 * AT - 0.18 * RH - 0.22 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15], PE is 579.01 - 2.05 * AP - 0.60 * AT - 0.05 * RH + 0.00 * V.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.02, output=Target.REGRESSION,\n", - " clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules (CREAM):\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 3.52, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 3.54, 2 rules\n", - "\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 3.52, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 3.48, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 3.50, 3 rules\n", - "\n", - "Algorithm.ExACT. Depth: 3. Threshold = 0.00. MAE = 3.44, 4 rules\n", - "Algorithm.ExACT. Depth: 3. Threshold = 0.00. MAE = 3.41, 4 rules\n", - "Algorithm.ExACT. Depth: 3. Threshold = 0.00. MAE = 3.52, 4 rules\n", - "\n", - "**********************\n", - "Best Algorithm.ExACT\n", - "**********************\n", - "MAE = 3.41, 4 rules\n", - "Threshold = 0.00\n", - "Depth = 3\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 3.41, 4 rules\n", - "Threshold = 0.00\n", - "Depth = 3\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 3.54, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 1\n", - "\n", - "CReEPy performance (4 rules with 100.00% coverage):\n", - "MAE = 3.37 (data), 3.46 (BB)\n", - "MSE = 18.48 (data), 19.00 (BB)\n", - "R2 = 0.94 (data), 0.94 (BB)\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 32.45], V in [35.39, 50.16], AP in [998.07, 1026.40], RH in [35.63, 100.10], PE is 499.89 - 2.16 * AP - 0.27 * AT + 0.01 * RH - 0.11 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 32.45], V in [34.02, 50.16], AP in [997.90, 1026.41], RH in [35.63, 100.10], PE is 697.90 - 1.74 * AP - 2.04 * AT - 0.17 * RH + 0.61 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 35.77], V in [25.35, 81.56], AP in [997.84, 1026.45], RH in [25.55, 100.12], PE is 234.73 - 1.42 * AP - 0.29 * AT + 0.26 * RH - 0.12 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15], PE is 628.20 - 2.19 * AP - 0.50 * AT - 0.09 * RH - 0.17 * V.\n" - ] - } - ], - "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.5,\n", - " algorithm=CRASH.Algorithm.ExACT, output=Target.REGRESSION)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 10.29, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 8.46, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 10.29, 2 rules\n", - "\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 6.46, 4 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 6.10, 4 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 7.75, 4 rules\n", - "\n", - "Algorithm.CREAM. Depth: 3. Threshold = 0.00. " - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_14404/1950581394.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mcrash\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCRASH\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmax_depth\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m3\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpatience\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreadability_tradeoff\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m.75\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0malgorithm\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mCRASH\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mAlgorithm\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCREAM\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 2\u001B[1;33m \u001B[0mcrash\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msearch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 3\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0m_\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0m_\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdepth\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthreshold\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcrash\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mget_best\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 4\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 5\u001B[0m creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\tuning\\crash\\__init__.py\u001B[0m in \u001B[0;36msearch\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 23\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 24\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0msearch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 25\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mparams\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__search_depth\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 26\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 27\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__search_depth\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\tuning\\crash\\__init__.py\u001B[0m in \u001B[0;36m__search_depth\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdepth\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mrange\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmax_depth\u001B[0m \u001B[1;33m+\u001B[0m \u001B[1;36m1\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__search_threshold\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdepth\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[0mb\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mOptimizer\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_best\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mp\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\tuning\\crash\\__init__.py\u001B[0m in \u001B[0;36m__search_threshold\u001B[1;34m(self, depth)\u001B[0m\n\u001B[0;32m 56\u001B[0m \u001B[0mclustering\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mClustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcream\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0malgorithm\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mCRASH\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mAlgorithm\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCREAM\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mClustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mexact\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 57\u001B[0m )\n\u001B[1;32m---> 58\u001B[1;33m \u001B[0m_\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mextractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 59\u001B[0m mae, n = (extractor.mae(self.dataframe, self.predictor) if self.objective == Objective.MODEL else\n\u001B[0;32m 60\u001B[0m extractor.mae(self.dataframe)), extractor.n_rules\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\creepy\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;32mraise\u001B[0m \u001B[0mTypeError\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34m\"clustering must be a HyperCubeClustering\"\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_hypercubes\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mget_hypercubes\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_hypercubes\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\exact\\__init__.py\u001B[0m in \u001B[0;36mfit\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 58\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 59\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_hypercubes\u001B[0m \u001B[1;33m=\u001B[0m\u001B[0;31m \u001B[0m\u001B[0;31m\\\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 60\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mNode\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 61\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget_hypercubes\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mHyperCube\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\cream\\__init__.py\u001B[0m in \u001B[0;36m_iterate\u001B[1;34m(self, surrounding)\u001B[0m\n\u001B[0;32m 52\u001B[0m \u001B[0mgauss_params\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mselect_gaussian_mixture\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgauss_components\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 53\u001B[0m \u001B[0mgauss_pred\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mgauss_params\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 54\u001B[1;33m \u001B[0mcubes\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__eligible_cubes\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mgauss_pred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnode\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgauss_params\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 55\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mcubes\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m<\u001B[0m \u001B[1;36m1\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 56\u001B[0m \u001B[1;32mcontinue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\cream\\__init__.py\u001B[0m in \u001B[0;36m__eligible_cubes\u001B[1;34m(self, gauss_pred, node, clusters)\u001B[0m\n\u001B[0;32m 27\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdf\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m==\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 28\u001B[0m \u001B[1;32mcontinue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 29\u001B[1;33m \u001B[0minner_cube\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdf\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mclusters\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 30\u001B[0m \u001B[0mindices\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_indices\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0minner_cube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnode\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mindices\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\exact\\__init__.py\u001B[0m in \u001B[0;36m_create_cube\u001B[1;34m(self, dataframe, clusters)\u001B[0m\n\u001B[0;32m 49\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_create_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mclusters\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mClosedCube\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 50\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mExACT\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_remove_string_label\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 51\u001B[1;33m \u001B[0mdbscan_pred\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mDBSCAN\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0meps\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mselect_dbscan_epsilon\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mclusters\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 52\u001B[0m return HyperCube.create_surrounding_cube(\n\u001B[0;32m 53\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mwhere\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdbscan_pred\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mCounter\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdbscan_pred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmost_common\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\utils.py\u001B[0m in \u001B[0;36mselect_dbscan_epsilon\u001B[1;34m(data, clusters)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0mepsilon\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmax\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdistances\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1e-3\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[0mk\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m1.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mdbscan_pred\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mDBSCAN\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0meps\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mepsilon\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mk\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[1;31m# while Counter(dbscan_pred).most_common(1)[0][0] == -1:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mi\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mrange\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m1000\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\cluster\\_dbscan.py\u001B[0m in \u001B[0;36mfit_predict\u001B[1;34m(self, X, y, sample_weight)\u001B[0m\n\u001B[0;32m 456\u001B[0m \u001B[0mCluster\u001B[0m \u001B[0mlabels\u001B[0m\u001B[1;33m.\u001B[0m \u001B[0mNoisy\u001B[0m \u001B[0msamples\u001B[0m \u001B[0mare\u001B[0m \u001B[0mgiven\u001B[0m \u001B[0mthe\u001B[0m \u001B[0mlabel\u001B[0m \u001B[1;33m-\u001B[0m\u001B[1;36m1.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 457\u001B[0m \"\"\"\n\u001B[1;32m--> 458\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msample_weight\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0msample_weight\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 459\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mlabels_\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 460\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\cluster\\_dbscan.py\u001B[0m in \u001B[0;36mfit\u001B[1;34m(self, X, y, sample_weight)\u001B[0m\n\u001B[0;32m 404\u001B[0m \u001B[0mneighbors_model\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 405\u001B[0m \u001B[1;31m# This has worst case O(n^2) memory complexity\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 406\u001B[1;33m \u001B[0mneighborhoods\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mneighbors_model\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mradius_neighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mFalse\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 407\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 408\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0msample_weight\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mradius_neighbors\u001B[1;34m(self, X, radius, return_distance, sort_results)\u001B[0m\n\u001B[0;32m 1167\u001B[0m \u001B[0mn_jobs\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0meffective_n_jobs\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1168\u001B[0m \u001B[0mdelayed_query\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdelayed\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0m_tree_query_radius_parallel_helper\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1169\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 1170\u001B[0m delayed_query(\n\u001B[0;32m 1171\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mradius\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort_results\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0msort_results\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1041\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1042\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1043\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1044\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1045\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 859\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 860\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 861\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 862\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 863\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 777\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 778\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 779\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 780\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 781\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 570\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 571\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 572\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 573\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 574\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 260\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 261\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 262\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 263\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 264\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 260\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 261\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 262\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 263\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 264\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\fixes.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 115\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 116\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 117\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 118\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 119\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_radius_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 949\u001B[0m \u001B[0mcloudpickle\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 950\u001B[0m \"\"\"\n\u001B[1;32m--> 951\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery_radius\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 952\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 953\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " - ] - } - ], - "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.75, algorithm=CRASH.Algorithm.CREAM)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "it = Extractor.iter(predictor, min_update=1.0 / 10, n_points=1, max_iterations=600,\n", - " min_examples=100, threshold=5)\n", - "theory_from_iter = it.extract(train)\n", - "scores, completeness = get_scores(it, test, predictor)\n", - "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 6.45, 15 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 6.45, 30 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 6.45, 86 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 6.45, 142 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 6.45, 144 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 6.45, 146 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 2)]). Threshold = 0.00. MAE = 6.45, 150 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 2)]). Threshold = 0.00. MAE = 6.45, 154 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 6.45, 157 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 6.45, 160 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 3)]). Threshold = 0.00. MAE = 6.45, 169 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 3)]). Threshold = 0.00. MAE = 6.45, 178 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 6.45, 183 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 6.45, 188 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 5)]). Threshold = 0.00. MAE = 6.45, 209 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 5)]). Threshold = 0.00. MAE = 6.45, 230 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 6.45, 240 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 6.45, 250 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 6.45, 256 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 6.45, 262 rules\n", - "\n", - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 6.45, 256 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridEx performance (277 rules with 99.90% coverage):\n", - "MAE = 6.50 (data), 6.49 (BB)\n", - "MSE = 66.41 (data), 65.91 (BB)\n", - "R2 = 0.77 (data), 0.77 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n" - ] - } - ], - "source": [ - "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL)\n", - "pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridEx = Extractor.gridex(predictor, grid, threshold=threshold)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 6.45, 256 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridREx performance (292 rules with 99.90% coverage):\n", - "MAE = 6.50 (data), 6.49 (BB)\n", - "MSE = 66.41 (data), 65.91 (BB)\n", - "R2 = 0.77 (data), 0.77 (BB)\n", - "GridREx extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85], PE is 462.71 - 2.15 * AP - 0.22 * AT + 0.04 * RH - 0.02 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 431.79 - 2.16 * AP - 0.45 * AT + 0.08 * RH - 0.06 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 495.74 - 1.80 * AP - 0.07 * AT + 0.00 * RH - 0.01 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 703.70 - 2.37 * AP - 0.12 * AT - 0.18 * RH - 0.11 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 540.25 - 2.54 * AP - 0.04 * AT - 0.04 * RH + 0.03 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 556.66 - 2.35 * AP - 0.04 * AT - 0.05 * RH + 0.05 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 453.81 - 2.40 * AP - 0.18 * AT + 0.05 * RH - 0.04 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85], PE is 495.08 - 1.30 * AP - 0.20 * AT - 0.00 * RH - 0.10 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 394.31 - 1.06 * AP - 0.32 * AT + 0.09 * RH - 0.01 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 625.50 - 1.14 * AP + 0.09 * AT - 0.13 * RH - 0.27 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 699.87 - 1.10 * AP + 0.06 * AT - 0.21 * RH - 0.05 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85], PE is 106.52 - 1.12 * AP - 0.21 * AT + 0.37 * RH - 0.04 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 180.91 - 1.23 * AP - 0.29 * AT + 0.31 * RH - 0.09 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 396.75 - 1.41 * AP - 0.28 * AT + 0.10 * RH - 0.01 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 446.80 - 1.76 * AP - 0.30 * AT + 0.07 * RH - 0.22 * V.\n" - ] - } - ], - "source": [ - "#pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - "# max_depth=2, patience=1, algorithm=PEDRO.Algorithm.GRIDREX, objective=Objective.MODEL)\n", - "#pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridREx = Extractor.gridrex(predictor, grid, threshold=threshold)\n", - "theory_from_gridREx = gridREx.extract(train)\n", - "scores, completeness = get_scores(gridREx, test, predictor)\n", - "print(f'GridREx performance ({gridREx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('GridREx extracted rules:\\n\\n' + pretty_theory(theory_from_gridREx))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (6 rules with 100.00% coverage):\n", - "MAE = 4.46 (data), 4.49 (BB)\n", - "MSE = 32.55 (data), 32.53 (BB)\n", - "R2 = 0.89 (data), 0.89 (BB)\n", - "CART extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, 479.15) :-\n", - " AT =< 18.25, AT =< 11.90.\n", - "'PE'(AP, AT, RH, V, 435.66) :-\n", - " AT > 18.25, V > 66.20.\n", - "'PE'(AP, AT, RH, V, 451.33) :-\n", - " AT > 18.25, V =< 66.20, AT =< 22.89.\n", - "'PE'(AP, AT, RH, V, 443.00) :-\n", - " AT > 18.25, V =< 66.20, AT > 22.89.\n", - "'PE'(AP, AT, RH, V, 467.45) :-\n", - " AT > 11.90, AT =< 15.64.\n", - "'PE'(AP, AT, RH, V, 459.70) :-\n", - " AT > 15.64.\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, max_depth=5, max_leaves=6, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('CART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "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.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index bcf9ca55..e0309801 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 12, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -17,7 +17,9 @@ "from psyke import Extractor, Clustering, Target\n", "from psyke.extraction.hypercubic.strategy import AdaptiveStrategy\n", "from psyke.extraction.hypercubic import Grid, FeatureRanker\n", - "from psyke.utils.logic import pretty_theory" + "from psyke.utils.logic import pretty_theory\n", + "\n", + "from plot import *" ] }, { @@ -95,7 +97,114 @@ "name": "stdout", "output_type": "stream", "text": [ - "2491\n" + "2491\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2492\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2493\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2494\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2495\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2496\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2497\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2498\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2499\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2500\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2501\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2502\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2503\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2504\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2505\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2506\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2507\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2508\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n" ] } ], @@ -130,7 +239,7 @@ " model = KNN(200, weights='distance', p=1).fit(scaledTrain.iloc[:, :-1], scaledTrain.iloc[:, -1])\n", " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", - " break\n", + "\n", " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", @@ -138,8 +247,7 @@ " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)\n", - " break" + " missed[name].append(miss)" ], "metadata": { "collapsed": false, @@ -150,74 +258,12 @@ }, { "cell_type": "code", - "execution_count": 19, - "outputs": [ - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_7300/1366764375.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mranked\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mFeatureRanker\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mrankings\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 2\u001B[1;33m CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n\u001B[0m\u001B[0;32m 3\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[0;32m 4\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mgridex\u001B[1;34m(predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 298\u001B[0m \"\"\"\n\u001B[0;32m 299\u001B[0m \u001B[1;32mfrom\u001B[0m \u001B[0mpsyke\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextraction\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mhypercubic\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgridex\u001B[0m \u001B[1;32mimport\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 300\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthreshold\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mseed\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 301\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 302\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 18\u001B[0m def __init__(self, predictor, grid: Grid, min_examples: int, threshold: float, normalization=None,\n\u001B[0;32m 19\u001B[0m seed=get_default_random_seed()):\n\u001B[1;32m---> 20\u001B[1;33m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCONSTANT\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 21\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgrid\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 22\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmin_examples\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " - ] - } - ], - "source": [ - "ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", - "CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n", - " threshold=5, min_examples=1, normalization=normalization)\n", - "CART.extract(scaledTrain)\n", - "p = CART.predict(scaledTest)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 17, + "execution_count": 39, "outputs": [], "source": [ - "CART.normalization" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 15, - "outputs": [ - { - "data": { - "text/plain": "(648,)" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.array(p).shape" + "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", + "pd.DataFrame(rules).to_csv('results/rules1.csv')\n", + "pd.DataFrame(missed).to_csv('results/missed1.csv')" ], "metadata": { "collapsed": false, @@ -228,19 +274,20 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 38, "outputs": [ { "data": { - "text/plain": "array([ 5.94787596e-01, 6.27852580e-01, 6.53351288e-01, 6.86756656e-01,\n 6.80554845e-01, 6.56268596e-01, 6.41509728e-01, 6.23215360e-01,\n 5.06968206e-01, 4.49547691e-01, 4.50112948e-01, 3.89409986e-01,\n 4.17987119e-01, 4.47363530e-01, 4.73589506e-01, 5.27920839e-01,\n 5.65069947e-01, 6.30275078e-01, 6.50014692e-01, 6.25667786e-01,\n 5.32084121e-01, 5.11315913e-01, 4.66818875e-01, 3.79488214e-01,\n 3.49910991e-01, 2.21345403e-01, 1.93290286e-01, 1.29067767e-01,\n 1.29278790e-01, 9.70285114e-02, 1.16471507e-01, 1.20058101e-01,\n 1.10625154e-01, 9.43989669e-02, 4.62561519e-02, 1.54927422e-02,\n -3.67243793e-02, -9.73570606e-02, -8.82136957e-02, -8.59049356e-02,\n -1.88867848e-01, -1.98258070e-01, -2.14678123e-01, -2.25880942e-01,\n -2.73753227e-01, -2.86873033e-01, -3.36773813e-01, -3.41153580e-01,\n -2.72939657e-01, -2.60564106e-01, -2.22866189e-01, -3.16103250e-01,\n -4.57077669e-01, -5.69617624e-01, -4.85024701e-01, -4.38443662e-01,\n -1.81425306e-01, 3.16564715e-02, 2.01502725e-01, 3.30505305e-01,\n 4.34886887e-01, 4.88884946e-01, 4.81334210e-01, 4.72318295e-01,\n 4.49382717e-01, 3.71368779e-01, 3.10463488e-01, 2.27584909e-01,\n 1.96014710e-01, 1.21620524e-01, 8.96840584e-02, 9.66028209e-02,\n 5.56857290e-02, 9.76252433e-02, 1.16906912e-01, 9.32035580e-02,\n 1.61667957e-01, 2.43306894e-01, 4.00764438e-01, 4.08452018e-01,\n 5.04550610e-01, 6.33769305e-01, 7.70897913e-01, 8.66805211e-01,\n 9.64611914e-01, 9.24679976e-01, 9.83779612e-01, 9.57014376e-01,\n 1.18224046e+00, 1.18196180e+00, 1.25001824e+00, 1.26741209e+00,\n 1.31358148e+00, 1.30732589e+00, 1.27459292e+00, 1.28298857e+00,\n 1.30996624e+00, 1.33409461e+00, 1.34064098e+00, 1.33456069e+00,\n 1.26926162e+00, 1.23751719e+00, 1.21728041e+00, 1.18781088e+00,\n 1.07904111e+00, 9.78348868e-01, 8.94449891e-01, 8.02642930e-01,\n 7.67312190e-01, 7.79881638e-01, 7.56459980e-01, 6.39844731e-01,\n 5.59816075e-01, 5.07320578e-01, 3.96137907e-01, 3.08059300e-01,\n 3.49538875e-01, 4.37822132e-01, 5.19863709e-01, 4.83896648e-01,\n 5.49159005e-01, 6.51009546e-01, 6.66199773e-01, 6.75533692e-01,\n 7.14284944e-01, 7.49667837e-01, 7.27343955e-01, 7.50315379e-01,\n 7.35121200e-01, 7.11411018e-01, 6.50371738e-01, 6.57852210e-01,\n 6.88303646e-01, 6.88771323e-01, 6.53846882e-01, 6.39351067e-01,\n 6.42534572e-01, 6.11897413e-01, 5.48695256e-01, 5.36093577e-01,\n 4.68813273e-01, 4.81517348e-01, 4.16924579e-01, 2.27269556e-01,\n 6.95082620e-02, 4.73304329e-02, 4.31446914e-02, 1.51598952e-03,\n -1.63872715e-01, -2.66752323e-01, -3.04611850e-01, -3.09940192e-01,\n -3.80448778e-01, -5.20578055e-01, -5.58929636e-01, -5.66185437e-01,\n -5.50515452e-01, -4.97476048e-01, -4.69612472e-01, -4.37668499e-01,\n -4.08758754e-01, -4.70707072e-01, -5.20063597e-01, -5.77177092e-01,\n -6.33710168e-01, -6.80554694e-01, -7.54318656e-01, -7.98758101e-01,\n -8.44280781e-01, -8.98363509e-01, -8.95358460e-01, -8.86503609e-01,\n -8.85599683e-01, -8.82890386e-01, -8.53614367e-01, -8.31532738e-01,\n -8.33123651e-01, -6.27513729e-01, -1.37548268e-01, 1.58522033e-01,\n 3.43769358e-01, 3.53969327e-01, 3.30450997e-01, 3.16298042e-01,\n 3.07723557e-01, 2.50430376e-01, 1.79257019e-01, 1.48908911e-01,\n 1.94124475e-01, 2.96394183e-01, 3.65626353e-01, 4.03952940e-01,\n 4.89904130e-01, 5.43864115e-01, 5.70022845e-01, 5.78188313e-01,\n 6.02378943e-01, 5.53382870e-01, 5.76370663e-01, 4.53366033e-01,\n 5.46894816e-01, 6.22814657e-01, 6.98452824e-01, 8.43786807e-01,\n 9.03241794e-01, 9.80655929e-01, 1.00069167e+00, 1.03552441e+00,\n 1.05781862e+00, 1.03407151e+00, 1.01060741e+00, 9.95349018e-01,\n 9.83748339e-01, 9.77971820e-01, 9.59232606e-01, 9.75587590e-01,\n 9.86886799e-01, 9.89016229e-01, 1.01371718e+00, 1.02127174e+00,\n 1.01666168e+00, 1.06380858e+00, 1.04014026e+00, 9.68632461e-01,\n 8.50561688e-01, 7.88313619e-01, 8.60332530e-01, 8.39510615e-01,\n 8.19082407e-01, 8.41616540e-01, 8.45556255e-01, 8.78204007e-01,\n 9.01526215e-01, 8.99408318e-01, 9.75302328e-01, 1.07239590e+00,\n 1.12769248e+00, 1.10865204e+00, 1.16262617e+00, 1.19097896e+00,\n 1.18783719e+00, 1.13230601e+00, 1.14472708e+00, 1.16748934e+00,\n 1.15556360e+00, 1.15497319e+00, 1.14661413e+00, 1.12722389e+00,\n 1.11759420e+00, 1.11397318e+00, 1.12588339e+00, 1.10233146e+00,\n 1.06748597e+00, 9.94411455e-01, 9.34246345e-01, 9.26761867e-01,\n 8.79439448e-01, 8.34147761e-01, 7.88468961e-01, 2.63101924e-01,\n 2.58441893e-01, 2.75837911e-01, 3.73833340e-01, 4.06661808e-01,\n 4.50858947e-01, 4.25019531e-01, 3.93777296e-01, 4.25283768e-01,\n 4.50114669e-01, 4.32118982e-01, 4.26723712e-01, 4.25086302e-01,\n 4.12253850e-01, 4.20772144e-01, 3.73951452e-01, 3.84730496e-01,\n 4.15677813e-01, 4.70782896e-01, 5.18673694e-01, 6.28786146e-01,\n 7.11168389e-01, 8.38840673e-01, 9.24391393e-01, 1.00049266e+00,\n 1.02386999e+00, 1.07021416e+00, 1.09681443e+00, 1.14425776e+00,\n 1.35203664e+00, 1.36045510e+00, 1.41445129e+00, 1.42926777e+00,\n 1.48989705e+00, 1.48908458e+00, 1.52517735e+00, 1.55774308e+00,\n 1.53297116e+00, 1.48283249e+00, 1.45336878e+00, 1.43025635e+00,\n 1.42162708e+00, 1.39982454e+00, 1.38697364e+00, 1.40470142e+00,\n 1.41465673e+00, 1.41616546e+00, 1.39234617e+00, 1.39974420e+00,\n 1.37379750e+00, 1.31108897e+00, 1.29610318e+00, 1.26624619e+00,\n 1.21507803e+00, 1.18620871e+00, 1.16431475e+00, 1.08350728e+00,\n 1.04351910e+00, 8.14857616e-01, 9.19351580e-01, 9.92161799e-01,\n 1.05183656e+00, 1.06302057e+00, 1.15394942e+00, 1.26940951e+00,\n 1.28522890e+00, 1.44338788e+00, 1.36122698e+00, 1.22730775e+00,\n 1.26271454e+00, 1.25683022e+00, 1.26641669e+00, 1.35523754e+00,\n 1.35363264e+00, 1.19374746e+00, 8.84222340e-01, 8.45086765e-01,\n 8.26652842e-01, 7.78535822e-01, 7.91430635e-01, 7.51209328e-01,\n 7.16570137e-01, 7.12982315e-01, 6.71535968e-01, 6.59427982e-01,\n 6.61309295e-01, 6.00194599e-01, 5.46063131e-01, 5.06583233e-01,\n 4.60146040e-01, 4.13683133e-01, 3.27972661e-01, 2.84937524e-01,\n 2.45562643e-01, 2.77127619e-01, 2.53966681e-01, 2.33893354e-01,\n 2.36568447e-01, 2.28037916e-01, 2.49038569e-01, 2.64400726e-01,\n 2.94056569e-01, 2.90707304e-01, 2.83857999e-01, 3.01860592e-01,\n 3.14841571e-01, 3.39277627e-01, 3.01466072e-01, 2.93500775e-01,\n 2.87430279e-01, 2.70941481e-01, 3.06142458e-01, 3.36453506e-01,\n 3.49177875e-01, 3.60445946e-01, 3.73710668e-01, 4.04441898e-01,\n 3.94960073e-01, 3.99827257e-01, 3.77291659e-01, 3.30380384e-01,\n 3.37732723e-01, 3.47186442e-01, 3.52084474e-01, 3.77269993e-01,\n 3.77988341e-01, 3.80380920e-01, 3.69822861e-01, 3.58249184e-01,\n 3.64697520e-01, 3.33135345e-01, 3.09091741e-01, 3.80991398e-01,\n 3.61867705e-01, 3.19234148e-01, 3.21624558e-01, 2.17452675e-01,\n 1.92589914e-01, 1.79242718e-01, 1.53009476e-01, 9.77992145e-02,\n 3.23604394e-02, -2.94612149e-02, -1.14045668e-01, -1.55128381e-01,\n -1.57616220e-01, -1.71918305e-01, -1.50894217e-01, -1.67564490e-01,\n -1.88622273e-01, -2.07652581e-01, -2.58115218e-01, -2.93005352e-01,\n -3.38270047e-01, -1.49019196e-01, -3.44280222e-01, -3.28079666e-01,\n -3.26221855e-01, -2.34694433e-01, -2.19842775e-01, -2.43728538e-01,\n -2.38287675e-01, -2.07637015e-01, -2.99053400e-01, -3.51944773e-01,\n -2.98335213e-01, -2.75216185e-01, -2.52291688e-01, -5.92295676e-01,\n -6.29698629e-01, -6.37034248e-01, -6.67193507e-01, -6.61285775e-01,\n -6.59767960e-01, -6.55582862e-01, -6.60398712e-01, -6.48089994e-01,\n -6.30606033e-01, -6.13985068e-01, -5.94299126e-01, -5.51793470e-01,\n -5.21829072e-01, -5.33152444e-01, -5.58533647e-01, -5.30664797e-01,\n -4.96012827e-01, -4.81793417e-01, -4.94722300e-01, -5.05906482e-01,\n -5.84771524e-01, -6.21891793e-01, -6.57652629e-01, -6.74505274e-01,\n -6.77925168e-01, -6.63411854e-01, -6.70761225e-01, -6.71313501e-01,\n -6.78285438e-01, -6.88366243e-01, -6.75084854e-01, -6.86645949e-01,\n -6.86878877e-01, -7.03152004e-01, -7.11550530e-01, -7.06169968e-01,\n -7.06030613e-01, -7.11075855e-01, -6.80750629e-01, -6.72296413e-01,\n -6.65302849e-01, -6.74474185e-01, -6.70617066e-01, -6.70406918e-01,\n -6.31489293e-01, -5.41195959e-01, -5.06100680e-01, -4.73968482e-01,\n -4.80026300e-01, -4.66280906e-01, -4.94750733e-01, -4.66159421e-01,\n -4.53704671e-01, -4.65426691e-01, -4.67830842e-01, -4.48499002e-01,\n -4.37999267e-01, -4.06635710e-01, -4.36565611e-01, -4.87644475e-01,\n -5.10015971e-01, -5.26553795e-01, -5.24534882e-01, -5.17204913e-01,\n -5.18734726e-01, -4.88314619e-01, -4.81448198e-01, -4.49654115e-01,\n -3.59544883e-01, -2.89470852e-01, -2.52844742e-01, -1.92796426e-01,\n 3.61641165e-02, 2.67556715e-01, 2.88468372e-01, 2.69765539e-01,\n 3.03003617e-01, 1.56430282e-01, 8.94392059e-02, 8.35489348e-02,\n 9.53608325e-02, 1.06359033e-01, 4.85854783e-02, 5.95200015e-02,\n 8.92134085e-02, 1.71655377e-01, -1.62529760e-01, -9.18029145e-02,\n -7.00795871e-03, 3.55558744e-02, 7.13713389e-02, 8.51286206e-02,\n 8.85206272e-02, 9.03776601e-02, 7.80484532e-02, 7.76571930e-02,\n 7.06369278e-02, 5.61746134e-02, 7.89197268e-02, 8.51250688e-02,\n 1.11657860e-01, 8.77699810e-02, 1.40152096e-01, 1.41833281e-01,\n 1.40840041e-01, 1.85234816e-01, 1.53057074e-01, 1.61289830e-01,\n 1.60056307e-01, 1.47913238e-01, 1.59117011e-01, 1.24454678e-01,\n 8.40377871e-02, 9.25309859e-02, 8.57329210e-02, 7.57535730e-02,\n 6.16577201e-02, 1.06938161e-01, 1.02192008e-01, 1.71101839e-01,\n 2.19090649e-01, 1.94452383e-01, 1.52915395e-01, 1.42999064e-01,\n 2.34941176e-02, -1.69292889e-02, -4.84760026e-02, -8.84035972e-02,\n -1.06477491e-01, -1.46097303e-01, -1.68977796e-01, -1.69546328e-01,\n -3.06123179e-01, -2.86830814e-01, -3.27339805e-01, -3.04824309e-01,\n -3.08424522e-01, -3.13009099e-01, -3.08204615e-01, -3.30643627e-01,\n -3.47108546e-01, -3.71663036e-01, -3.69800392e-01, -3.81838621e-01,\n -4.11920074e-01, -4.26296448e-01, -4.33323503e-01, -4.32084854e-01,\n -4.26418566e-01, -3.99080526e-01, -3.43601617e-01, -3.32431572e-01,\n -3.17056043e-01, -2.77115852e-01, -2.52202840e-01, -2.14129148e-01,\n -2.00518462e-01, -1.46597547e-01, 2.36017486e-01, 2.72685842e-01,\n 3.48128210e-01, 2.84062462e-01, 2.81911796e-01, 2.71269801e-01,\n 2.17936171e-01, 1.81334194e-01, 1.55982915e-01, 1.36845118e-01,\n 8.53148103e-02, 5.47081435e-02, -1.14402861e-02, -5.94431312e-02,\n -9.09789281e-02, -1.44687840e-01, -1.67931366e-01, -1.76196230e-01,\n -1.84964598e-01, -1.80156999e-01, -1.82591858e-01, -2.04167736e-01,\n -2.17966566e-01, -1.25904259e-01, -1.53099147e-01, -1.62306106e-01,\n -2.16528907e-01, -2.69388887e-01, -3.25151001e-01, -5.16529016e-02,\n 2.34635280e-01, 2.86861522e-01, 2.49274427e-01, 1.65100897e-01,\n 1.31739752e-01, 1.38150405e-01, 1.26449068e-01, 1.62529445e-01,\n 1.71290348e-01, 1.77941660e-01, 1.68343103e-01, 1.21794429e-01,\n 7.66499527e-02, 9.79244195e-02, 1.13147159e-01, 1.19400540e-01,\n 1.31218961e-01, 1.32184095e-01, 1.21069556e-01, 1.14229094e-01,\n 1.29832401e-01, 1.24523142e-01, 1.39992356e-01, 1.42168147e-01,\n 2.48498740e-01, 2.74234518e-01, 3.09358436e-01, 3.08036727e-01,\n 3.27261828e-01, 3.36394705e-01, 3.42936055e-01, 3.48891465e-01])" + "text/plain": " BR GridEx GridREx CART COSMiK CReEPy\n0 2491 1 13 7 5 2\n1 2492 14 14 7 6 2\n2 2493 1 14 7 7 2\n3 2494 3 14 7 5 2\n4 2495 21 14 7 7 2\n5 2496 25 8 7 7 2\n6 2497 21 37 7 7 2\n7 2498 3 12 7 8 2\n8 2499 5 14 7 9 2\n9 2500 1 13 7 6 2\n10 2501 3 14 7 5 2\n11 2502 3 14 7 4 2\n12 2503 1 5 7 4 2\n13 2504 3 14 7 7 2\n14 2505 3 14 7 6 2\n15 2506 3 14 7 7 2\n16 2507 4 12 7 7 2\n17 2508 1 5 7 6 2", + "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
BRGridExGridRExCARTCOSMiKCReEPy
02491113752
124921414762
22493114772
32494314752
424952114772
52496258772
624972137772
72498312782
82499514792
92500113762
102501314752
112502314742
12250315742
132504314772
142505314762
152506314772
162507412772
17250815762
\n
" }, - "execution_count": 16, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "model.predict(scaledTest)" + "pd.DataFrame(rules)" ], "metadata": { "collapsed": false, @@ -251,20 +298,24 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "outputs": [ { - "data": { - "text/plain": " index V model GridEx GridREx \\\n0 2016-03-04 00:00:00 410.0 518.750997 0.0 -0.381030 \n1 2016-03-04 01:00:00 400.0 522.352090 0.0 -0.407892 \n2 2016-03-04 02:00:00 395.0 525.129143 0.0 -0.433331 \n3 2016-03-04 03:00:00 408.0 528.767307 0.0 -0.461158 \n4 2016-03-04 04:00:00 406.0 528.091871 0.0 -0.477063 \n.. ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 0.0 -0.294504 \n644 2016-03-30 20:00:00 501.0 489.614882 0.0 -0.278289 \n645 2016-03-30 21:00:00 518.0 490.609540 0.0 -0.267642 \n646 2016-03-30 22:00:00 510.0 491.321955 0.0 -0.251590 \n647 2016-03-30 23:00:00 511.0 491.970557 0.0 -0.229831 \n\n CART \\\n0 [441.86581920903956, 441.86581920903956, 441.8... \n1 [441.86581920903956, 441.86581920903956, 441.8... \n2 [441.86581920903956, 441.86581920903956, 441.8... \n3 [441.86581920903956, 441.86581920903956, 441.8... \n4 [441.86581920903956, 441.86581920903956, 441.8... \n.. ... \n643 [441.86581920903956, 441.86581920903956, 441.8... \n644 [441.86581920903956, 441.86581920903956, 441.8... \n645 [441.86581920903956, 441.86581920903956, 441.8... \n646 [441.86581920903956, 441.86581920903956, 441.8... \n647 [441.86581920903956, 441.86581920903956, 441.8... \n\n COSMiK \\\n0 [340.3877551020408, 340.3877551020408, 340.387... \n1 [340.3877551020408, 340.3877551020408, 340.387... \n2 [340.3877551020408, 340.3877551020408, 340.387... \n3 [340.3877551020408, 340.3877551020408, 340.387... \n4 [340.3877551020408, 340.3877551020408, 340.387... \n.. ... \n643 [340.3877551020408, 340.3877551020408, 340.387... \n644 [340.3877551020408, 340.3877551020408, 340.387... \n645 [340.3877551020408, 340.3877551020408, 340.387... \n646 [340.3877551020408, 340.3877551020408, 340.387... \n647 [340.3877551020408, 340.3877551020408, 340.387... \n\n CReEPy \n0 [464.19181456754364, 462.0928005366921, 459.64... \n1 [464.19181456754364, 462.0928005366921, 459.64... \n2 [464.19181456754364, 462.0928005366921, 459.64... \n3 [464.19181456754364, 462.0928005366921, 459.64... \n4 [464.19181456754364, 462.0928005366921, 459.64... \n.. ... \n643 [464.19181456754364, 462.0928005366921, 459.64... \n644 [464.19181456754364, 462.0928005366921, 459.64... \n645 [464.19181456754364, 462.0928005366921, 459.64... \n646 [464.19181456754364, 462.0928005366921, 459.64... \n647 [464.19181456754364, 462.0928005366921, 459.64... \n\n[648 rows x 8 columns]", - "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.7509970.0-0.381030[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
12016-03-04 01:00:00400.0522.3520900.0-0.407892[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
22016-03-04 02:00:00395.0525.1291430.0-0.433331[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
32016-03-04 03:00:00408.0528.7673070.0-0.461158[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
42016-03-04 04:00:00406.0528.0918710.0-0.477063[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
...........................
6432016-03-30 19:00:00497.0487.5210850.0-0.294504[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6442016-03-30 20:00:00501.0489.6148820.0-0.278289[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6452016-03-30 21:00:00518.0490.6095400.0-0.267642[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6462016-03-30 22:00:00510.0491.3219550.0-0.251590[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6472016-03-30 23:00:00511.0491.9705570.0-0.229831[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
\n

648 rows × 8 columns

\n
" - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "89.05338024185028 58.58037289200033\n", + "1184045968753.4443 1184045968728.9656\n", + "69.78300681133707 39.32044206472971\n", + "99.34258321344431 70.77190676931815\n", + "65.09043620900334 30.05875280438403\n" + ] } ], "source": [ - "pd.DataFrame(predicted)" + "p = pd.DataFrame(predicted)\n", + "for e in extractors:\n", + " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())" ], "metadata": { "collapsed": false, @@ -275,10 +326,30 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "outputs": [], "source": [ - "sdsfgd" + "def plot(testB, x, pred, extracted):\n", + " missing = pd.read_csv(\"data/missing.csv\", parse_dates = [0], index_col = 0)\n", + " df = pd.read_csv(\"data/averaged1.csv\", parse_dates = [0], index_col = 0)\n", + " df.LPFnorm[missing.index] = np.nan\n", + "\n", + " for i, b in bartels.iterrows():\n", + " if b.n != testB:\n", + " continue\n", + " xminmax = [b.t0, b.t1]\n", + "\n", + " f, axes = plt.subplots(3, figsize=(10, 8)) # l x h\n", + "\n", + " myplot(axes[0], [3, 1], \"LPF\", df.index, df.LPFnorm, xminmax)\n", + " myplot(axes[0], [3, 1], \"\", missing.index, missing.LPF0, xminmax, color='r', marker='*', lw=0, size=10)\n", + " myplot(axes[1], [3, 2], \"V\", df.index, df.V, xminmax)\n", + " myplot(axes[1], [3, 2], \"\", x, pred, xminmax, color='b', marker='.', lw=0)\n", + " myplot(axes[1], [3, 2], \"\", x, extracted, xminmax, color='r', marker='.', lw=0)\n", + " myplot(axes[2], [3, 3], \"B\", df.index, df.B, xminmax)\n", + " plt.subplots_adjust(hspace=0.6)\n", + " plt.savefig(f\"plot/{b.n}.jpg\", dpi=96 * 2)\n", + " plt.show()" ], "metadata": { "collapsed": false, @@ -289,39 +360,21 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "pd.DataFrame(predicted).to_csv(\"pred.csv\")" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" + "execution_count": 37, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmUAAAHoCAYAAAAISZi8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAC8/klEQVR4nOzdd3iTVfvA8e/pYG8om7JEioAI4kYRwYELnOB4X30dIODg97r1VUFF2SAbRGQoLlQEFFRky95lbyi0tEDLKB20zf37I8MkTdukTdoU7s915Wr7JDnPSZrnyf2ccR8jIiillFJKqaIVUtQVUEoppZRSGpQppZRSSgUFDcqUUkoppYKABmVKKaWUUkFAgzKllFJKqSCgQZlSSimlVBAokqDMGBNmjHnXGDOpKPavlFJKKRVsiqqlrCywwL5/Y0w3Y8yLxphBxpjSRVQnpZRSSqkiUyRBmYicAU45bXpcRMYA64EHiqJOSimllFJFKayoK2BTyvbzBHC9+53GmB5AD4CyZcteHRUVVYhVU0oppZTKnw0bNpwUkQhvHhssQVma7WcEcMT9ThGZBEwCaNu2raxfv74Qq6aUUkoplT/GmMPePraoBvoboBvQ1BjTBphpjHkRaAv8XBR1UkoppZQqSkXSUibWVdAH2W4AG4uiHkoppZRSwULzlCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDMqWUUkqpIKBBWRBauHAht956a1FXQymllFKF6KILylasWEGtWrX4448/HNv279/PTTfdxAcffABA//79GT9+POPHj+eJJ55wef61117L3Llzs5UbHx/P/fffT8+ePZk4cSJPPvkk8fHxvPnmm9x6661MnjyZyZMn89RTT7k8z37/559/zssvv8x3332X52vo1KmT4/epU6eyb98+j487dOgQTz/9tOPv//3vf3mW7cnRo0e5//77ueeeexzbRITrrruOnj17kpyc7HVZQ4YM4cyZMwAaWCqllFI+CJbksX7Trl07KlasyB133OHY1rhxY5o0aULnzp0B+Pnnn1m8eDGVK1fm5ptvdjxu8eLFPPnkkwwfPpz77rvPpdwaNWrQpk0boqKi6N69OwcOHGDNmjV07tyZM2fO8NxzzzFw4ECmTZvm8jz7/c8//zxbt27l008/pVu3bkyfPp2UlBROnTpFhw4duPHGG/nkk08QEWrXrg3AhQsXWLZsGQD169fntddeo02bNuzevZtPPvmEZcuWsXfvXqZMmcJ1113HDz/8wMcff8z27duZPn06TZo04ejRo7z11lt0796diIgIIiMj2b9/P1OnTnXUsW7durRp04Zjx44xf/58OnfuzHfffUdUVBQdOnSgXLlyjBkzBmMM8fHxdOzYkZIlS9K9e3fee+89Fi1axKOPPsrNN9/M8uXLueaaawgJCSEuLo7Jkyfz+OOPM2TIEOrWrcvu3bt56qmniImJoW/fvvTt25cpU6YwfPhwFixYQPXq1SldujTPP/+8Xz8XSimlVLC76IIyb0yYMIHXX3+dpKQkOnToQIsWLQBYsGABgwYNYt68eWzYsIHWrVvz8ssvExoaymeffQbA0qVLSU5Opnr16txzzz0sX76c6OhoRo4cyd69ez3ub/fu3YwbN45ff/2Vzz//HIAmTZqwcuVKSpYsyfTp02nZsiXz5s1j5cqVJCcnM23aNEqUKMEtt9ziKKdu3bpYLBaWLVvG8ePHueWWW1i0aBHPPPMMALVq1QLgo48+on///jRt2pRu3bpx6NAhunbtyrlz53jppZdo3769x3oOGDCALl26cMMNN3Dy5EkaNmwIwPnz5/n2229ZsWIFKSkpdOzYkVWrVtGgQQNHMDZkyBC6dOlCmzZtALjllluoVasWzz33HLt27WLXrl188MEH7Ny5k/79+/Ptt99Srlw5evbsydNPP82ff/7J/v37uf3222nZsmVB/8VKKaVUsXPRB2V79+6lQYMG2bZPnjwZEeHOO++kXbt2lCxZkpMnTzJ16lSaN2/O0KFD+eabbxgzZozL89q3b0/37t1dtrVs2ZK+ffvmWIemTZvSu3dvUlNTmTt3Lj179qRXr16sXbuWmJgYPvroozxfx7x58zh9+jSvv/46ixYtIi0tDevCCGCxWBy/21nz8+KyvXz58tm2OatevToPP/wwjz76KD/99BNDhw51lOWpPHuZp06dIiMjI1t5xhhEBIvF4vH55cqVwxhDqVKluO6667jmmmuYOnUqX3/9NZMmTcrzPVFKKaUuJhddULZixQpOnz7tCChWr17N22+/zd69e5k/fz7XX389kydPZsuWLWRmZlKvXj1q1KjBiy++yNtvv03btm3ZtWsXN998M7Nnz6Zr166AdUzZxo0biYuLo0OHDtSoUQOA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VWP58uXExsbSqFEjJkyYAMALL7xA9+7dqVevHjExMaxbt462bdtSvnx5Nm7cSOXKlXOsT3h4uMtA9uHDh3PkyBE+/vhjwNr1+ddff2GMYd++fTRu3NhRxpo1a1iyZAl79+71ODjXLj09nbZt27J582bHtvj4eE6ePElERAQXLlygQ4cOrFixgg8//JCff/6Zl156iaVLl3rzFjscOHCAwYMH06ZNG+rWrUv58uXZv38/1atXp1q1amRlZXHnnXcycuRIoqOjueqqq8jKysIYQ2pqquNE/9prr9GgQQNH92/16tWpW7cuMTEx7Nmzh6eeeoqjR4+SkpLC7NmzHf9D5/fX/tpat27NSy+9xHPPPUeDBg14+umnadWqFU899ZTj8RqYqcJ2/vx5ypYtS506dYiPj6ds2bKcP38esI7BjI+P93qc5759+1iyZAmZmZmEh4fzyiuvEBERQb169TzmJqtWrZrLONvy5ctzzTXXuGTwr1WrFnXr1nV5Xtu2bbn22msdf4uIy4XeyZMnqVatWp71HTFiBN988w0nTpzIcViFunT4kjw2S0SGicijInK3iHQRkR4iEpgRmcrvTp48SdWqVTHGsGHDBgDHiU9EWLFihSP/zr333kuPHj3IyspyBGbdu3fn/vvvp0+fPpQsWZL27duzY8cOjh07Ru3atR2Pq1SpEqGhoVSsWJGzZ886lijp1q0bGzZs4KGHHnLUyVMAULduXbZs2UJqaio1a9Zk9+7dpKWlERcXx7x589i6dSvGGNLT02ncuLHjeWfPniUzM5P69etn6wJNSUlxnDCTkpK4/fbbXVrf3n//fU6cOEHjxo05e/YsJUqU4NixY3Tt2pXx48fz0EMPsWHDBo+zTHOydu1a3njjDa6++mrAetW8bt06atSoQadOnbjnnnuIioqiXbt2zJkzh1atWjlaCa+77joee+wxAK655hrHe2tXr149jh49yv79+2nUqBFVq1bllVde4c8//6RWrVrAPwGWMYY9e/Zw+eWXO7aHhITQtWtX6tSpA8Cdd94J/DOrTKnCdOTIESIjI6lXrx41a9akWbNm7Nq1y3E8v/766yxfvjzPcpKSkrjttttISEigWbNmLvd17NiR48ePZ3uOMYbjx487WuiefPJJbrnlFp9fw80338yyZcscf69du5arr77a44Xwzp07uXDhAgsWLOC2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7sS9td2+7qKdPZs8WINvzX9mVadOnTwnaNh7Bt5//31q1KjBiRMnaNasGVdcccUlEZAB+Q7IwDrm1fn7olatWvTv358rr7yShx9+mCVLlrg8fuLEiS7dv8ePHy/UtWt//vnnoDw+pk+f7rI4fKDs378/x+Xr8puE29fuy8+Aj0TkGRH5D/BOvvaa3U/ANcaYF4Gvc3vguXPnHCdMsLYaLV26FLCeXDMyMkhJSSE6OprGjRvTrFkz4uLiWLNmDSNGjHAMVhURIiIiOHnyJG+++SZHjx51yfnl7ueff+b111/n6aefZvr06Zw8edLxhWSf/eisZMmSpKenk5qa6tXyNnYlSpTgyJEj2bo3nV122WUuubvat29/yZzwgkmJEiUuyZmt0dHRXH755SQlJVGhQgVKlixJ5cqVeeSRRwDrlbx9hvCUKVP473//63hurVq1qF69OqGhoaSkpNC/f38sFgubNm3KFizZzZ8/n99++41GjRpx8OBBbrrpJubPn++yDundd9+d7Xn2hemdvzRmzJjBpk2bXCYL2FfjsAd0VatWdYz1XLZsWbbALticP3+ed955h4ULF2b70vbGokWLHF8g9nOps2PHjpGZmUmdOnU4fPgwv/76K3FxcR7LsndfhoSEONZn7dChA3fccYfP9boUlStXzmX8WHh4uKNl0hjDtddey88//8xnn32GiFCxYkWXC5Tjx487xnYWhjlz5uT4WShKxpiA5EJ0FxsbS8OGDbNNzvv222/p379/vsr0KSgT61nrGqdNnfO11+zlpojIGyIyJrdB/mC9Snc+cRw5coRTp06xePFihgwZwssvv8zOnTtJTk7mueeeo2vXrlgsFqpXr86YMWMIDw93ZGS3n3yvvfZaXnrpJTZt2sT69euzLW1RtWpVYmJiKFOmDBUqVKB69eps3ryZFi1aULNmTb+3Uh07dizHNdrAejV1//33O/5+++23/bp/pdyFh4ezfft2PvjgA/766y86dOhAQkICkZGR1K5dm//85z+O4yAqKoro6GjHBUnZsmUd5TRp0oTWrVvz8MMP06tXL1q2bMl3333n6NJ0t2HDBi5cuMD3339PZGQkVatWpUyZMowePZodO3Y4voBeeuklwDq+zDnlyUMPPcTMmTMB6/iPunXrcvDgQaZOncqJEydc9pWYmEjVqlVdgrKTJ0+6rKIRjObM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AQ3KlFJKKaWCgAZlSimllFJBQIMypZRSSqkgEBboHRhjWgMvAauAKsAhIAKoB/QDjO3nESBeRH4IdJ2UUkoppYKNEZHA7sCYSsA44ADQATgpIl2MMY8A4Vhb69JF5AdjzGwR6eqhjB5AD4CyZcteHRUVFdA6K6WUUkr5w4YNG06KSIQ3jw14SxlwHzAX+B5YBrxq234CuB5rS9kq27bSngoQkUnAJIC2bdvK+vXrA1lfpZRSSim/MMYc9vaxhTGmrBqQJCJZwOtAmm17BNYuyxjb7wCphVAfpZRSSqmgUxgtZd8BbxljmgDRwBpjzIu4jSkzxtQAvi6E+iillFJKBZ2AB2UiEgu8nMfD3gh0PZRSSimlgpmmxFBKKaWUCgIalCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDshwkJiZy//33s2TJkoDva/LkyTz99NMB349SSimlgtdFF5S98cYbdO7cmaSkJLZu3cp1110HwObNm+nevTsnTpzgww8/zLOcKlWq0KZNm0BXF4BOnToVyn6UUkopFbwKI09ZoXr//fe56667qFy5MtOnT6dKlSrExMQQEhJCz5492b9/P3PmzOH999/nhRdeICEhgeuuu46VK1fy888/ExMTwwcffMBNN93Epk2buPXWW13K79evH3Xr1mXz5s288cYb9OvXj/T0dG6++WZWrVrF2LFjOXz4MLNmzaJBgwZs27aNQYMGMWvWLE6cOAGAMYbevXvzf//3f9SrV4+srKwieKeUUkopFUwuupaycuXKUb16dQ4cOEBmZibdu3dn1qxZrFixgltuuYXrr7+ecuXKAdC9e3caNmzIm2++SdmyZYmLi2Ps2LF0796d559/niZNmmQr/8CBA5w5c4ZevXpRq1YtbrnlFm688UZeeOEFWrduzffff89HH31EmTJlEBFSU1OJi4ujX79+lC1blrJlyxIdHc2OHTuIi4vjv//9L507dy7st0kppZRSQeaiaykDeOihh+jfvz+PP/441113HQ8++CAPPvggoaGh2R5bvnx5AEqUKEFGRkaeZQ8dOpRDhw7x2muv8c4777jcZ1/c3RhDp06daN26NU2aNKFq1aoA/Pvf/yYkJIR69eoV9CUqpZRS6iJzUQZl9913H2+88QZffPEFYWFhlC5dmiuuuAKA1atXExcXx+rVq1myZAkbN25k7969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eTInTpww+by4f/8+Q4cOZePGjfTq1euBxzdu3LjAG2128oqjoyPnz5/X3zRz8thjj2nSJeDKlSv5GuPFldTUVL0HNqeBHRgYyLFjx4x+JiMjg5kzZzJlyhQ6duyIg4MD5cqV45tvvmHEiBF06dKFgIAAGjZsyBdffMG4ceM0e0Av6CGgfv36nDlzxuIxQJdYExUVRalSpXjsscdYvXo1TZo00e9v164dW7Zseeg7bZiLMspszM2bN/ONDQkODubAAW17vm/atIlu3brRtGlT+vbty/Tp07l//z6bNm2ia9eugC7oesuWLUydOpXmzZszcuRIxo4dy7179/LUtPnxxx/1nzOGq6urPgC+dOnSDBgwgKFDh+Lh4YGbmxvvvPMOLVq0ICQkpFD/j2bNmtGpUyeOHTvGzp07jR6TlpbGihUrjLr/c9+swsPD6dq1KxcuXAAKt9ySk379+tGsWTN9PODhw4cZOXIk27Zto1+/fkVaYsASpJT6uEYpZaEuoFJKZs2axTPPPMOhQ4c4efIkf/75JwEBAfTt27fAz2bHXRmLK7p165bZRlmpUqUYOHAgJUuWNLo/KSmJ1atXm5URmk22J8/d3T3fxJBs/P39efvtt0lNTWXp0qUmyf/777955ZVXTC4b0bJlS37//fd896empupj3hwdHalWrVqeY/z8/IiMjLTYi5KcnIy7u7tFMuyNI0eO6GvC5cxqbNGiBbt37yYhISHPZ+bOncvgwYOZOHGiPnO2c+fOpKSkGJybzs7OfPzxx5QuXZru3btr4i2LiYkxGioDuvp/WpWr2LNnDy+99BIAlStX5t69e7i6uur3CyEYPnw406dPL5axg9ZGGWU25v79+/lmWtapU4dz587pvQ1acP36df2yR7ly5RgwYADz588nOjpav7wghOC9995j4MCBBksOL730EocPH9ZP3piYGIQQeHp6WqRTlSpV9B6SbO7fv2/Ue7Zu3ToaNmyof//hhx9y8eJFAH7//XcWLlzIpEmTOHfuHDNmzKBVq1Zs3bqVmTNnkl14OCYmho8//tjgInTy5EmefPJJ/c00MTHRrKBygFatWrF//35A99RYvnx5wsLCaNy4sdUKNVqDatWqcenSJbZs2cKCBQtM/tzevXtp3749vr6+vPnmmxw/fpyffvqJVq1a6YP886NChQpERERQt27dPMtmd+7c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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "pd.DataFrame(rules).to_csv('rules.csv')\n", - "pd.DataFrame(missed).to_csv('missed.csv')" ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], "source": [ - "p = pd.DataFrame(predicted)" + "plot(2492, p['index'], p.model, p.GridREx)" ], "metadata": { "collapsed": false, @@ -335,7 +388,22 @@ "execution_count": null, "outputs": [], "source": [ - "abs(p.COSMiK - p.model).mean()" + "[\n", + " ('half2', 1.0),\n", + " ('Bmin', 0.9014926374248455),\n", + " ('Bmedian', 0.5931779258652916),\n", + " ('Bmax', 0.5580664110760513),\n", + "\n", + " ('trend', 0.441328536097858),\n", + " ('max', 0.19391808653199277),\n", + " ('median', 0.16157357528452515),\n", + "\n", + " ('halfB2', 0.06672545822303622),\n", + " ('min', 0.042323799412967836),\n", + " ('B', 0.014457395044254808),\n", + " ('halfB1', 0.005159557201584083),\n", + " ('half1', 0.0023286124765232786),\n", + "]" ], "metadata": { "collapsed": false, @@ -346,10 +414,25 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 15, + "outputs": [ + { + "data": { + "text/plain": "V 454.232082\nmodel 454.416913\nCART2 55.474434\nCART5 39.939136\nCART10 37.642059\nCART25 36.371025\nCART50 39.203983\nCART75 40.454190\nCART100 43.945508\nCART150 47.059375\nCART200 49.097016\nCART250 51.038554\nCART300 51.182619\nCART400 52.651320\nCART500 52.874986\nCART600 53.924574\nName: mean, dtype: float64" + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "p.describe()" + "import pandas as pd\n", + "\n", + "p = pd.read_csv('results/cartpredpruned.csv')\n", + "p = p[p.columns[2:]]\n", + "for c in p.columns[2:]:\n", + " p[c] = abs(p[c] - p.model)\n", + "p.describe().loc['mean']" ], "metadata": { "collapsed": false, From 02306f4aaa4e9e23251a4317cea8c2cc724bbdac Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 01:49:39 +0200 Subject: [PATCH 27/67] wip --- demo/DemoRegressionScaled.ipynb | 331 +++++++++++++------------------- 1 file changed, 132 insertions(+), 199 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index e0309801..3c90fa85 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 12, + "execution_count": 1, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -22,6 +22,40 @@ "from plot import *" ] }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [], + "source": [ + "def plot(testB, x, pred, extracted):\n", + " missing = pd.read_csv(\"data/missing.csv\", parse_dates = [0], index_col = 0)\n", + " df = pd.read_csv(\"data/averaged1.csv\", parse_dates = [0], index_col = 0)\n", + " df.LPFnorm[missing.index] = np.nan\n", + "\n", + " for i, b in bartels.iterrows():\n", + " if b.n != testB:\n", + " continue\n", + " xminmax = [b.t0, b.t1]\n", + "\n", + " f, axes = plt.subplots(3, figsize=(10, 8)) # l x h\n", + "\n", + " myplot(axes[0], [3, 1], \"LPF\", df.index, df.LPFnorm, xminmax)\n", + " myplot(axes[0], [3, 1], \"\", missing.index, missing.LPF0, xminmax, color='r', marker='*', lw=0, size=10)\n", + " myplot(axes[1], [3, 2], \"V\", df.index, df.V, xminmax)\n", + " myplot(axes[1], [3, 2], \"\", x, pred, xminmax, color='b', marker='.', lw=0)\n", + " myplot(axes[1], [3, 2], \"\", x, extracted, xminmax, color='r', marker='.', lw=0)\n", + " myplot(axes[2], [3, 3], \"B\", df.index, df.B, xminmax)\n", + " plt.subplots_adjust(hspace=0.6)\n", + " plt.savefig(f\"plot/{b.n}.jpg\", dpi=96 * 2)\n", + " plt.show()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": 2, @@ -48,22 +82,22 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 30, "outputs": [], "source": [ "def gridex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridEx = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3), (0.75, 4)])),\n", - " threshold=5, min_examples=1, normalization=normalization)\n", + " threshold=.5, min_examples=1, normalization=normalization)\n", " gridEx.extract(train)\n", " return gridEx.brute_predict(test), gridEx.n_rules, sum([p is None for p in gridEx.predict(test)])\n", " \n", "def gridrex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n", - " threshold=5, min_examples=1, normalization=normalization)\n", + " threshold=.5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", - " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", + " return gridREx.brute_predict(test, 'default'), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", @@ -71,13 +105,13 @@ " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", "def cosmik(model, train, test, normalization):\n", - " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", - " output=Target.CONSTANT, normalization=normalization)\n", + " COSMiK = Extractor.cosmik(model, max_components=10, k=150, patience=10, close_to_center=True,\n", + " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", - " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", + " return COSMiK.brute_predict(test, 'default'), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", "def creepy(model, train, test, normalization):\n", - " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", + " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" @@ -91,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 31, "outputs": [ { "name": "stdout", @@ -102,110 +136,33 @@ "GridREx\n", "CART\n", "COSMiK\n", - "CReEPy\n", - "2492\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2493\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2494\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2495\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2496\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2497\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2498\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2499\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2500\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2501\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2502\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2503\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2504\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2505\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2506\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2507\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2508\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", "CReEPy\n" ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/3576493649.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/2214823521.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 34\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n\u001B[0;32m 35\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 380\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 381\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 382\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 383\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + ] } ], "source": [ @@ -240,54 +197,16 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", + " for name, fun in zip(extractors, [gridex, cart, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 39, - "outputs": [], - "source": [ - "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", - "pd.DataFrame(rules).to_csv('results/rules1.csv')\n", - "pd.DataFrame(missed).to_csv('results/missed1.csv')" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 38, - "outputs": [ - { - "data": { - "text/plain": " BR GridEx GridREx CART COSMiK CReEPy\n0 2491 1 13 7 5 2\n1 2492 14 14 7 6 2\n2 2493 1 14 7 7 2\n3 2494 3 14 7 5 2\n4 2495 21 14 7 7 2\n5 2496 25 8 7 7 2\n6 2497 21 37 7 7 2\n7 2498 3 12 7 8 2\n8 2499 5 14 7 9 2\n9 2500 1 13 7 6 2\n10 2501 3 14 7 5 2\n11 2502 3 14 7 4 2\n12 2503 1 5 7 4 2\n13 2504 3 14 7 7 2\n14 2505 3 14 7 6 2\n15 2506 3 14 7 7 2\n16 2507 4 12 7 7 2\n17 2508 1 5 7 6 2", - "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
BRGridExGridRExCARTCOSMiKCReEPy
02491113752
124921414762
22493114772
32494314752
424952114772
52496258772
624972137772
72498312782
82499514792
92500113762
102501314752
112502314742
12250315742
132504314772
142505314762
152506314772
162507412772
17250815762
\n
" - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pd.DataFrame(rules)" + " missed[name].append(miss)\n", + "\n", + " break" ], "metadata": { "collapsed": false, @@ -298,24 +217,69 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 24, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "89.05338024185028 58.58037289200033\n", - "1184045968753.4443 1184045968728.9656\n", - "69.78300681133707 39.32044206472971\n", - "99.34258321344431 70.77190676931815\n", - "65.09043620900334 30.05875280438403\n" + "75.32443332103857 45.80166313557053\n", + "65.44680302846326 41.183118869594864\n", + "65.44680302846326 41.183118869594864\n", + "82.47813971748161 33.34135118914507\n", + "80.09439063082522 39.23197875360054\n" ] + }, + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADP6UlEQVR4nOydd3gU5dbAf29C7713aSKINAERkCsWrlf97Fx7R4OiXntHQYqooPSOoCiKBaVI7713QugtAUINCSFlz/fH7C67yW6yLQ3O73nmSaadOTsz78yZ8573HCMiKIqiKIqiKLmPsJxWQFEURVEURfGMGmqKoiiKoii5FDXUFEVRFEVRcilqqCmKoiiKouRS1FBTFEVRFEXJpaihpiiKoiiKkkvJdkPNGFPJGDPGGLPWZVkhY8wQY8z7xphxxpj62a2XoiiKoihKbiMnPGo3A9MA47LsdeCQiPQFBgJjc0AvRVEURVGUXEW2G2oiMhWIS7P4LmClff1WoKkxpkR266YoiqIoipKbyC0xahVwN97O25cpiqIoiqJcteTLaQXsnACKu8yXsC9LhzHmReBFgKJFi7Zo2LBh1munKIqiKIoSJOvXr48VkfL+7JNbDLUZQFtgqTGmCbBZRM572lBERgGjAFq2bCnr1q3LPi0VRVEURVECxBhz0N99cmLUZ0fgCaCyMeYjY0xh4FugpjHmI+BN4Lns1ktRFEVRFCW3ke0eNRFZDCz2sKp7duuiKIqiKIqSm8ktgwkURVEURVGUNKihpiiKoiiKkktRQ01RFEVRFCWXooZaLmfevHnccsstOa2GoiiKoig5wBVtqC1btozKlSszZ84c57K9e/fSrl07Pv30UwA+++wzhg8fzvDhw3nsscfc9r/xxhv5+++/08k9fvw499xzD926dWPkyJE8/vjjHD9+nHfffZdbbrmFMWPGMGbMGJ566im3/RzrR48eTY8ePZgyZUqmv6Fz587O/ydMmMCePXs8bnfgwAGefvpp5/xHH32UqWxPHDlyhHvuuYe77rrLuUxEaN26Nd26dePChQs+yxowYADnzp0DUGNTURRFUQIgt+RRyxJuvvlmSpYsye233+5cds0111CvXj26dOkCwB9//MHChQspXbo07du3d263cOFCHn/8cb755hvuvvtuN7kVK1akefPmNGzYkK5du7Jv3z5Wr15Nly5dOHfuHM8//zz9+vXj+++/d9vPsf6FF15gy5Yt9O3bl0ceeYSJEyeSkJDAqVOn6NSpEzfddBN9+vRBRKhSpQoASUlJLFmyBICaNWvy1ltv0bx5cyIjI+nTpw9LliwhKiqKcePG0bp1a3799Vd69+7N9u3bmThxIvXq1ePIkSO89957dO3alfLly1OjRg327t3LhAkTnDpWq1aN5s2bc/ToUWbNmkWXLl2YMmUKDRs2pFOnThQrVowhQ4ZgjOH48ePceuutFCxYkK5du/Lxxx+zYMECHn74Ydq3b8/SpUtp1aoVYWFhREdHM2bMGB599FEGDBhAtWrViIyM5KmnnuLw4cO8/vrrvP7664wbN45vvvmGf/75hwoVKlC4cGFeeOGFkN4XiqIoipJXuKINNV8YMWIEb7/9NmfOnKFTp040btwYgH/++Yf+/fszffp01q9fT7NmzejRowfh4eF8++23ACxevJgLFy5QoUIF7rrrLpYuXcrWrVsZNGgQUVFRHo8XGRnJsGHDmDFjBqNHjwagXr16rFixgoIFCzJx4kSaNGnC9OnTWbFiBRcuXOD777+nQIECdOjQwSmnWrVq2Gw2lixZQkxMDB06dGDBggU8++yzAFSuXBmAXr168dlnn9GgQQMeeeQRDhw4wP/93/8RFxfHq6++SseOHT3q+cUXX3DvvffStm1bYmNjqV27NgDx8fH8/PPPLFu2jISEBG699VZWrlxJrVq1nAbagAEDuPfee2nevDkAHTp0oHLlyjz//PPs2rWLXbt28emnn7Jz504+++wzfv75Z4oVK0a3bt14+umnmTt3Lnv37uW2226jSZMmwV5iRVEURcmzXFWGWlRUFLVq1Uq3fMyYMYgId9xxBzfffDMFCxYkNjaWCRMmcN111/HVV1/x008/MWTIELf9OnbsSNeuXd2WNWnShNdff92rDg0aNCAiIoKLFy/y999/061bN15++WXWrFnD4cOH6dWrV6a/Y/r06Zw9e5a3336bBQsWkJiYiDEGAJvN5vzfgYgAuC0vXrx4umWuVKhQgQcffJCHH36Y33//na+++sopy5M8h8xTp06RnJycTp4xBhHBZrN53L9YsWIYYyhUqBCtW7emVatWTJgwgR9//JFRo0Zlek4URVEU5UrkijbUli1bxtmzZ51GxqpVq3j//feJiopi1qxZtGnThjFjxrB5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As7tmzRpQt++ff0+jmss2qxZs/jf//6HMYZy5cppnFoOsG/fvgyNqcy6XGw2GzNmzACsJMoOL63DUFu9ejVr164NSLeTJ09SvXp1Lly4AMDSpUvZtGmTx21nz57N6tWrAzqOooSSY8eOeVyekJCQYajIu+++y44dOyhdujQbN250Lj906BARERF8++23rFy5MuT6ZsSqVauc///zzz/s3r07W4+vKMFyRRlqN9xwAydPniQlJYWCBQuyd+9ewsIu/8Q6derQpEkTt33KlCnjk0fN0zbr1q2jdevWzvny5ctTpkwZSpUqFXR3meI7ixYtYsOGDR6Xr1+/niFDhvDXX3+lW3/mzBmGDx/Oxo0b+eOPPwDLUKtZsybGGJKTk6lUqRIzZ85k0aJFbp4Eh+GVGbt27eK2227jyJEjgBWv5i3P3sWLF93SzBw6dMinY4SSQA1S5crio48+8vjMK1eunNf7NzY2lqioKPbt28djjz3G1q1bnesOHjxI48aNefvtt9NV+7h06VI6WaHMc/nuu+86B5qdOXOGPXv2hEy2omQHV5ShVq1aNT744AMAKleuzK5duyhRooRz/Z133smNN96Ybr98+fJl+GCYMGECPXr0QESIiYmhXLlyAKxevZp27do5t6tRowa1a9emWbNmbl+TvrBs2TIdjOAjf//9t9v8xYsX2b9/f7rzt2vXLtatW0fVqlXdKgwsXryYJUuW8MMPP9CwYUOGDBlCy5YtSU5Opnr16tSuXZtChQpx8eJFSpYsydChQ+nYsaMzkHr//v289957PukaFRVFp06dnIZaWFiYVyPeGON8oaSmpvLMM89w5swZ305KiPjzzz+z9XhK7iQ2NtZjVY7y5ctz8uRJfvvtN1JSUtzuz82bN9O+fXtOnTpFiRIluHjxIgMGDEBEOH36NKVLl6ZEiRJuXf82m41HHnkEESEhIQGbzQaQrusUrLbkL+fOnePGG290etUKFy7M8ePH/ZajKDlJUIaaMaa0MaaGfeoZIp2C0YeaNWs6/9+xYwfXXpt5tpDatWt7HJm3c+dOvvzySxo0aMA999xDVFQU8+fP59Zbb3U7poOqVavStm1b6tat6/dX2y+//OK3cXe1Mnr0aETE2T2TL18+UlNTeeONN9J9nZ88eZIyZcqQL9/lammRkZHs2LGDfPny0alTJ+666y5at25NWFgYXbt2pWbNmlSrVo0jR44476nGjRuzbds2RIRffvnFp/sKrJx7tWvXdhpq4eHhzpeRJxz306FDh+jRowdTpkxJt01iYmLAQd2ZsXfv3iyRq+QdkpKSaN68OVu3buWnn35ydlWKCOXLl+f48eMMHDiQ+fPnuw2U2bNnD/feey+7dl0e+L9gwQKnYWSMwRjj9kF16NAhbrrpJubOncu3337Ltm3bAFixYgUi4pwfP348f/75J9u3b3dLSp0ZkZGRdO3alS1btjh1cDB69Gh/T42i5AgBG2rGmLHAUmAC8D3wRIh0ChrHgyAuLo4GDRpkur2j9mdaFixYwFtvvUXbtm1p164dK1as4Ny5c5QqVYp8+fJRsmRJj/LCwsKcL+PExESfupMaNmzoFkuhWHgKPr548SKxsbG89dZbzmU2m418+fKxYMECt21TUlK48cYbKViwIImJic5tU1JSnN3iDz74IE2bNqVr167OZTVq1HDreixSpAgXL15k4sSJ3HfffYSHh3vVOW05s0KFCjmP7WDixInMnDkz3b7FihUjPj6eyMhImjZt6jE34Pbt21m8eLHX4ztYt25dptukZf/+dOkRlauMPXv20KlTJ2bNmkX58uWdYQXnzp2jVq1abNmyhaZNm7JgwQK6du3KgAEDSExMJCUlhUaNGlG5cmXAev62bNkyQ0/Ytm3bePTRR9m6dSuJiYlERUWRnJzM6dOniYmJYezYscTExJAvXz7eeustRo8ezahRozzK8pTbcvfu3TRo0MDNQHO8HyZMmKBVZJQ8QTAetZIi0lhE/iUinYDnQqVUsCQmJlKkSBGefPJJChcunOn2Du+JK47G7HhxFylShHPnztGoUSPAGojQqVMnrzId+3///fcsW7YsUx1cY+mUy3z11Vdu86dPn+aGG25g9erVnDt3zmnInDhxgkceeYR9+/YBllcgf/78vPXWW9SqVYsaNWq4deUcP37czYgPCwujTJkyzvlq1aqlG4SQmJjIxYsXqV+/PmXKlPHYhZmQkMCAAQMAiI+Pd6ZtsdlsznvC0c3jMAQPHjzoTBFTpUoVoqOjOXToEDVq1KBy5crpAru3bdtG3bp1Mz1348aN86mbZ926dc6PhOTkZO2Cv0LxVrc2bSjBjh07aNSoEWFhYdx6662EhYWRmprKqVOnKF++PJGRkTz//PMUL16cpk2b8tBDDzFz5kzCw8MpWLAgPXv2BKyPz+eee469e/d6TUZ+5MgRqlatyn/+8x/+7//+j9jYWI4dO0ajRo3YuHEjSUlJzJw5kwcffBBjDIMGDaJSpUoeZU2ePNn5/5w5czh79qwzabXjNzhISEigZcuWrF+/3p9TqOQBLl68SLdu3XJajZASjHWw3RhTzGW+dLDKhIpixYpRpUoV7r33Xp+2DwsLS/dy2rhxI82bN3db9tprr3HLLbcA0KpVK6pWrepVZtWqVTl48CA2my3THGunTp2iTJkyHvW42knrZdy5cyddunRh9uzZPPzww8yePZtSpUrx+OOP06JFC2ec17x582jfvj3FihXDGEONGjU4ePAgZ8+eBaBLly60aNHC63ELFy6c7voWLVqU++67D/DuhT127JjzGKtWrXLGRObPn5/Fixdz3XXXObd1dAPNnj3b2Z1TpUoVjh07hs1mIywsjNtuu425c+cCMGbMGI4dO0ZCQgINGjRI93GRlooVK7J8+fIMtwGr0samTZu4dOkSpUqVSuf9U64MevfunW7ZyZMnGTlypNuy48ePU758eb777juMMbRq1Yr169cTGxtLuXLlOHbsGE2aNOGjjz4CoFatWvz666/861//Aqx7HeA///kPtWrVYuvWrdSvX9/tGOvWrWPgwIEcPnwYYwwNGjSgWbNmgPXhctNNN7FkyRLq1KlDYmJiug9uEXG7Ty9duuTW5Tp37lxWrFjhNM6uvfZadu7cCUCJEiXYtGkT999/v4abXGH06tWLBQsW0KpVK48DzPIqwRhqzwAnjDH7jTH7gRyv++mgbt261KpVy699RMQtv86aNWs8Djzwlfbt29O3b19uvvnmTLeNioqiQYMGTm/KpUuXeP3111m6dGnAx8/LHDp0yO0h7Bp8vHv3bm666SZWrVrFQw89xN69e7n77rupW7cuYWFhzi7qyMhIt5dD9erVWbJkCX369KFp06bcdNNNmdbxTBuH9sILLzhTvdSrV8/jMH+HN2D48OEcO3bM6bW79957+fXXX2nfvr1z2zJlynDmzBmn9w8uG2oOg7148eJcuHCBDRs2UK1aNX7++WdiY2O5+eabnV09K1as8Kh/pUqVfM4GLyKcPHmSOnXqcOHChYC6TbOb2bNn57QK2ULarnR/mTt3LsnJyR5Tv/zzzz9u96Qnrr32WiIjIzl16hRl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pQrx8fEcOXKEm2++mb179zpTqzjSyjhGnDk8AImJic5uya1bt3L99dc747Tat2/Pb7/9xo033sjmzZt58cUXadmyZbpjlyhRwtkV6Mi/FxMTQ5UqVVi3bh0333wzBw4ccHaZO9qNa1deWFgYd911V6bpODxx00030bhxY7/3y26ee+45v7vny5UrR61atXjkkUfo3LkzgFtC6+joaCpVquQ0Ijyl9UlISHCO6q5bty533XWX1wTDine2b9/OuHHjuPfeezl+/DiLFy/mqaeeIjY2FhEhMjKSxMREdu3axf79+2nWrFm6soEOwsPDSU1NxRjjzFHqaBc2m401a9bQvn37DEdg79mzhzp16jhjTtetW5ft9ZchCI+aiFwAPnDMG2PaAMHUQDoBuLaAEvZlaY87ChgF0LJlS8koaWluIyUlhfHjx1OpUiXatWtHpUqVMMZ4Tby6fv36dOtWr15NeHi423JHfqPU1FSGDx/Oli1bvJZZSU1NZd++fRkme02LL7FhIsKlS5ecgcibN2+madOmbNu2DZvNRsmSJWnfvj0iwueff86QIUOoWLEi9957L7///judOnVyJpN18Mgjj/ilZ07QoEEDxo8fT9euXZ0v7OykWbNm3Hbbbdx3333ky5ePhg0b0rZtW6exn5YmTZrw73//O8PKBvny5aNdu3bExMRw+vRp5zXYu3cvzZo1o2TJkjn60o6Pj6dhw4a0aNGCtWvXsn79egoVKuT1Xjlx4gRhYWHpYo/8ZenSpdxzzz0sXbqUAgUK8H//939s3bqVmjVr0qJFCxYsWMA///xDt27dnCPPNm3axIIFCyhUqBClSpUiKSmJBx98kHXr1hEfH88tt9zCddddR61atZg6dapPsYlgJVMODw+nQYMGzJkzhxtvvJHffvuNl156yWkgOkrXuRp+ub095RYcsWbNmzdn7dq13HLLLURGRlK9enUqV67svPcc53Px4sWULl2amjVr8sILL7B48WIqVqzotR0qntm0aRNjxoyhYMGCHDlyhJSUFJ555hkeeOAB/v77b1atWkVUVBRPPfUU+/bt47PPPmPHjh1e7+u//vqLdu3akZCQQNmyZWnUqBE2m41Nmzbx3nvvERMTQ3h4OBUrVvSYwH7NmjU0bNiQokWL0qhRI5o0aUKBAgWcMrxlHwg1fnvUjDHd7X/HuU7AkCB1WQnU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PXva9wUFBREeHk6bNm246667rlp6sWLFimRNeN+lSxcmTZrEnXfeSZMmTbySq69pi1oh2Lx5s0ProbmifPnyOZqPHZGamsrYsWPp3r07bdu2ZdSoUcV+nBW4dlduq1atWL9+PU8//TS1a9f2ynJKV/rnP//p9deIiYmxD/T9888/mTx5Mn/88YdLEza/9NJLrFq1iunTp9O1a1eHnyciVKhQgaCgIBo3bpzvsVFRUVy4cIHjx487tEB8VFQUtWvXtrd2uGPRokX079+fGjVq8J///MfetbV06VJKlCjBc889R5cuXXjrrbdo1KgRgwcPZsaMGfbF7Hv27Mlzzz3HK6+8wvHjx3n33XeJioqiT58+DB069KrXa968+VWzxLvj3LlzV21bsWIFr776KgMGDKB3795069aNcePGcf78eVatWkXlypXtPQTh4eEebU0+ceIEvXr14sUXX6RXr16sXr06x/6ff/7ZoYXAr7R792772MbsLl68SP/+/Rk7diyHDh0iPDzcvi8qKsrloSUHDhygTp06VKpUiZ07d5KUlESZMmWoU6cOw4cPdylmYbtw4YJ9iShvMMawYsUK7r333jyPKWh97BIlStCnTx+2bNnCgQMHmDNnjqfT9Dkt1Lxs06ZN1KpVy2vxAwIC2LFjB9OmTeOLL75g/PjxTJgwIddjFy5cyMSJE5kxYwbPPPMMkZGRNG7cmBdeeKHITwrqCa5OSDx48GCqVavGXXfdlWP805YtW3Idv3Py5EmGDx/O1KlTnX6tkydPEhMT49UbSObOncu5c+f4/PPPycjIYObMmTRo0ID169dTooTzjfAiwosvvshrr73m1M83MjKSWrVqMXbsWIdeN/vAe0fcf//9/PDDD5w9e5bXX3+do0ePOpzbiRMn+PHHH/noo48oVaoUISEh3H777bRv355p06Yxfvx4goKCcm05CQwMpEuXLsycOZOHH34YsLp0AwICuPfeexkwYAAdO3YEyLX1sWbNmvY1Fd2VkZGR65x3hw4dytF6GhYWRv/+/Xn77be5ePEijz32mH1f5cqVPT5OVkQICwvLdYmy5ORkoqKiSExMZN26dbz33nv25cYuXLhg71rMPkmwMQZjDJGRkbl2NYeFhTFw4EAuXLiQ4/Pj6l3dp06d4sSJE4SEhNCxY0cOHTrEI488AkC7du2IiIgoEncvLliw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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADQHUlEQVR4nOydd3gU5dbAf29C7713aSJIlyYi13696mfn2jsaFfVasaIiRSwghN4ERbFjAem9Se8l9JbQS0gIKXu+P2Z32U12k63JBs7veeZJpp05OzPvzJnznvccIyIoiqIoiqIokUdUXiugKIqiKIqieEYNNUVRFEVRlAhFDTVFURRFUZQIRQ01RVEURVGUCEUNNUVRFEVRlAhFDTVFURRFUZQIJdcNNWNMFWPMaGPMCpdlRYwxQ4wxPY0xY40xDXNbL0VRFEVRlEgjLzxqVwNTAOOy7GVgn4j0Bb4ExuSBXoqiKIqiKBFFrhtqIvITkJhp8a3AUvv6DUBzY0yp3NZNURRFURQlkoiUGLVKuBtvZ+zLFEVRFEVRLlkK5LUCdo4AJV3mS9mXZcEY8wzwDEDx4sVbN27cOPzaKYqiKIqiBMmqVauOiUhFf/aJFEPtL6ADsNAY0wxYJyJnPG0oIiOBkQBt2rSRlStX5p6WiqIoiqIoAWKM2evvPnkx6rML8DBQ1RjzrjGmKDAIqG2MeRd4FXgyt/VSFEVRFEWJNHLdoyYi84H5HlY9n9u6KIqiKIqiRDKRMphAURRFURRFyYQaaoqiKIqiKBGKGmqKoiiKoigRihpqEc6sWbO49tpr81oNRVEURVHygIvaUFu0aBFVq1ZlxowZzmU7d+6kU6dOfPDBBwB8+OGHDBs2jGHDhvHggw+67X/VVVfxxx9/ZJF7+PBhbr/9drp3786IESN46KGHOHz4MG+++SbXXnsto0ePZvTo0Tz66KNu+znWjxo1ih49ejB58uQcf8P111/v/H/8+PHs2LHD43Z79uzhsccec86/++67Ocr2xIEDB7j99tu59dZbnctEhHbt2tG9e3fOnj3rs6wBAwZw+vRpADU2FUVRFCUAIiWPWli4+uqrKV26NDfeeKNz2WWXXUaDBg245ZZbAPj111+ZO3cuZcuWpXPnzs7t5s6dy0MPPcQXX3zBbbfd5ia3cuXKtGrVisaNG9OtWzd27drF8uXLueWWWzh9+jRPPfUU/fr14+uvv3bbz7H+6aefZv369fTt25f777+fCRMmkJyczPHjx+natSsdO3akT58+iAjVqlUDIDU1lQULFgBQu3ZtXnvtNVq1asW2bdvo06cPCxYsIC4ujrFjx9KuXTt+/PFHevfuzaZNm5gwYQINGjTgwIEDvPXWW3Tr1o2KFStSq1Ytdu7cyfjx45061qhRg1atWnHw4EGmTZvGLbfcwuTJk2ncuDFdu3alRIkSDBkyBGMMhw8f5rrrrqNw4cJ069aN9957jzlz5nDffffRuXNnFi5cSNu2bYmKiiI+Pp7Ro0fzwAMPMGDAAGrUqMG2bdt49NFH2b9/Py+//DIvv/wyY8eO5YsvvuDvv/+mUqVKFC1alKeffjqk94WiKIqi5BcuakPNF4YPH87rr7/OyZMn6dq1K02bNgXg77//pn///vz555+sWrWKli1b0qNHD6Kjoxk0aBAA8+fP5+zZs1SqVIlbb72VhQsXsmHDBgYOHEhcXJzH423bto2hQ4fy119/MWrUKAAaNGjAkiVLKFy4MBMmTKBZs2b8+eefLFmyhLNnz/L1119TqFAhrrnmGqecGjVqYLPZWLBgAQkJCVxzzTXMmTOHJ554AoCqVasC8PHHH/Phhx/SqFEj7r//fvbs2cP//d//kZiYyIsvvkiXLl086vnJJ59wxx130KFDB44dO0bdunUBSEpK4vvvv2fRokUkJydz3XXXsXTpUurUqeM00AYMGMAdd9xBq1atALjmmmuoWrUqTz31FFu3bmXr1q188MEHbNmyhQ8//JDvv/+eEiVK0L17dx577DFmzpzJzp07ueGGG2jWrFmwl1hRFEVR8i2XlKEWFxdHnTp1siwfPXo0IsJNN93E1VdfTeHChTl27Bjjx4/niiuu4LPPPuO7775jyJAhbvt16dKFbt26uS1r1qwZL7/8slcdGjVqRExMDOfOneOPP/6ge/fuPPfcc/zzzz/s37+fjz/+OMff8eeff3Lq1Clef/115syZQ0pKCsYYAGw2m/N/ByIC4La8ZMmSWZa5UqlSJe655x7uu+8+fvnlFz777DOnLE/yHDKPHz9OWlpaFnnGGEQEm83mcf8SJUpgjKFIkSK0a9eOtm3bMn78eL799ltGjhyZ4zlRFEVRlIuRi9pQW7RoEadOnXIaGcuWLaNnz57ExcUxbdo02rdvz+jRo1m3bh3p6enUrFmTypUr88ILL9CzZ0/atGnD1q1b6dy5M7/99hv/93//B1gxaqtXryY+Pp6uXbtSuXJlAKZNm8bWrVtZvXq105vkimP91q1b6dGjB126dKFGjRrccccdfPTRRxQsWJC4uDgSExP597//Te/evSlTpgzx8fGsW7fO2fXZq1cvxo4dy5gxY9i4cSPz5s3j/vvv58SJE7z66qvcc889xMfHM2vWLN59913Gjh1Lo0aNaNy4MY0aNeLTTz8FoFOnTsTHxzNv3jxnDJnjt02bNo0ePXpw++23k5SU5Py9d9xxB926dSM2NpYjR47Qr18/tm3bRnx8PAsWLGDPnj3ExcVx6NAhVq9eTXp6Otdeey1ly5bl7bff5oknnqBx48aMHj2auLg43nvvPVatWkV8fDwzZszgxhtvZOrUqSQnJ1OkSBGP51FRFEVRLhWMw7uRH9Fan4qiKIqi5BeMMatEpI0/+1zUoz4VRVEURVHyM2qoKYqiKIqiRChqqCmKoiiKokQoaqgpiqIoiqJEKGqoKYqiKIqiRChqqCmKoiiKokQoAeVRM8ZcDjwFNAGKAvuAn0VkSgh1UxRFURRFuaTx21AzxtwH3AtMB2YBaUA54FpjzK0i8kxoVVQURVEURbk08ctQM8ZEAYjIvR5W/2CMudIYc4WIbApEGWPMMiDFPpshItcFIkdRFEVRFOViwC9DTURswA+uy4wxhYHCInJGRNYHqc/fItIrSBmKoiiKoigXBUHV+jTG/B/wDHDWGLNJRD4MUp9mxpg3seLeVojIX0HKUxRFURRFybcEEqN2rYjMs882E5F/25e/HAJ9+ovIP8aYaGCBMSZRRBZkOv4zWMYhtWrVCsEhFUVRFEVRIpNA0nN0NcYMMcaUAuKNMWOMMZ8C9YJVRkT+sf/NABYCXT1sM1JE2ohIm4oVKwZ0nHHjxgWlp3LpcOrUqbxWQVEURbmE8dtQE5EPgOHA18BRoDfwrYj0CEYRY0xjY8yTLosaADuz2ycxMTGgY82ePZtjx44FtG9OLF68mD179oRFtpL7jBgxIq9VUBRFUS5hAk14ux94CMuL9j4QHwJdzgC3GmPeM8YMsB9jUnY7pKRYA0QnTpzo14HKly/Pli1bAlTTO+np6ezbt4+DBw+GXLaSN+zduzevVVAURVEuYQKJUfsIuBwoAowGfgUGG2Omicj4QBURkUPAXf7sk56eDsCYMWO49957KVKkiE/7NWjQgLi4ODp37uy3nt44e/Yso0aNolChQhQrVixkcpW8w2azsX///rxWQ1EURbmECcSjdkZE7hWR27AGE+wRkftDrZgvREdHc/r0aa644gq2bdvm0z4iQsGCBUlLSwupLjt37uTw4cOISNi6VZXcJTk5OeDudUVRFEUJBYEYarWMMZ8aY0YBCY6FwXjTAsUYwz///MPjjz/O5s2bfdrn+PHjVKhQAREJqS47d+6kRIkSFCxY0OnpU/IXx44dIz7+Qi/+2bNnKV68eB5qpCiKolzqBDKYoAfwLdBHREaHXiX/2Lx5M61bt+b06dNetzl37hxDhgwB4MCBA9SoUYMCBQqQmpoa0DFTU1NJSkpyW3b06FECHYWqRAbr169nxYoVnDhxArAMtZIlS+axVoqiKMqljF+GmjEmyhhzi4isE5HdHtbXsxdszxWioqIoXbo0xhiSkpLIyMjwuN3cuXM5f/48O3bscBpq7dq1459//gnouP/88w9z587NstwYE5A8JTI4fPgw8fHxDBgwgDNnznD27FlKlCgRcu+roiiKoviKX4aavYRUNWPMSGPM7caY1saY5saYrsaYt4EPga1h0dQDpUuXplu3bgA8+OCDfP311x632717Nz169GDevHnEx8dTpUoVrrjiCjZs2EBaWhrHjx/367i7du3iyJEjWZbrCz1/c+bMGUSEpKQk1q9fT2JiItWqVePMmTNBydV0LcrFwrp16wLeNyEhIeeNFEXJQiBdn2OAicC9wCisFBpvAMeAxyQXrZUCBQo4R3pWqVLFY1fm+fPniYqKcsaOZWRkEB0dTVRUFGlpafzxxx/8/PPPfh03OTnZLQ5t3759OtLzIsEYQ8OGDdm6dStnz56lVq1azq7QQPn0009DpJ2i5C3fffddQPvZbDZiYmJCrI2iXBoElEdNRBaKyMMi0kpErhCRW+wVAzz3PeYimzZtYsGCBUycOJFz587xyy+/cNddF7J+uNqRXbt2Zfr06UF5wjZv3sxPP/3EQw89RMGCBQOOe1MiB4dRHwpDTUTcuthPnTrl1UOXkJCg948S0cTFxWU7WOr06dMcPnyYPn36uC0/fPgwhQoV4ujRo+FWMVu0jSn5kUAT3kYk1atX57vvviMuLo5Nmzaxa9cuTp06ReXKlQHrq65QoULO7Zs1a0bfvn39Po5rLNq0adP43//+hzGGChUqaJxaHrBr165sjamculxsNht//fUXYCVRdnhpHYba8uXLWbFiRUC6HT16lJo1a3L27FkAFi5cyNq1az1uO336dJYvXx7QcRQllBw6dMjj8uTk5GxDRd588002b95M2bJlWbNmjXP5vn37iImJYdCgQSxdujTk+mbHsmXLnP///fffbN++PVePryjBclEZai1atODo0aOkp6dTuHBhdu7cSVTUhZ9Yr149mjVr5rZPuXLlfPKoedpm5cqVtGvXzjlfsWJFypUrR5kyZYLuLlN8Z968eaxevdrj8lWrVjFkyBB+//33LOtPnjzJsGHDWLNmDb/++itgGWq1a9fGGENaWhpVqlRh6tSpzJs3z82T4DC8cmLr1q3ccMMNHDhwALDi1bzl2Tt37pxbmpl9+/b5dIxQEqhBqlxcvPvuux6feRUqVPB6/x47doy4uDh27drFgw8+yIYNG5zr9u7dS9OmTXn99dezVPs4f/58FlmhzHP55ptvOgeanTx5kh07doRMtqLkBheVoVajRg3efvttAKpWrcrWrVspVaqUc/3NN9/MVVddlWW/AgUKZPtgGD9+PD169EBESEhIoEKFCgAsX76cTp06OberVasWdevWpWXLlm5fk76waNEiHYzgI3/88Yfb/Llz59i9e3eW87d161ZWrlxJ9erV3SoMzJ8/nwULFvDNN9/QuHFjhgwZQps2bUhLS6NmzZrUrVuXIkWKcO7cOUqXLk1sbCxdunRxBlLv3r2bt956yydd4+Li6Nq1q9NQi4qK8mrEG2OcL5SMjAwef/xxTp486dtJCRG//fZbrh5PiUyOHTvmsSpHxYoVOXr0KD///DPp6elu9+e6devo3Lkzx48fp1SpUpw7d44BAwYgIpw4cYKyZctSqlQpt65/m83G/fffj4iQnJyMzWYDyNJ1ClZb8pfTp09z1VVXOb1qRYsW5fDhw37LUZS8JChDzRhT1hhTyz71CpFOwehD7dq1nf9v3ryZyy/POVtI3bp1PY7M27JlC59++imNGjXi9ttvJy4ujtmzZ3Pddde5HdNB9erV6dChA/Xr1/f7q+2HH37w27i7VBk1ahQi4uyeKVCgABkZGbzyyitZvs6PHj1KuXLlKFDgQrW0bdu2sXnzZgoUKEDXrl259dZbadeu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i3Pdr79S86+/SH/qKUY1b86hQ4d45513+P777zl9+jTly5fnoYce8uu3XIzs3LmTn3/+mVKlSpGRkcHZs2eJiYnx6dr4yvPPw9ChF+ZjYiA21lp++dDneZYRDKc7PUwszz0HV1wxlJiYmJAdX1EUJa/Yv38/EydO5IUXXqBUqVKA5bAZMWIEp0+fJioqitdff51vvvmG66+/nipVquSxxkHi+sCPjob09DxVxxizSkTa+LWTrxYdcCOWJ+0z4DBWotvHgaL+Woehmvz1qDnI7Lk6fvy4fPHFF8753377Tb766iuP8ThTpkyx4jqy8bc7A1iNEdtzz0nKU085vXAZUVEhicW6mElKSnJ2gTqKS4e6S9TX7pLoaP8LaSuKokQyycnJMmjQIFm+fLmIWBUedu3aJWfOnJGhQ4fKqVOnInOwQKDkVXZbDxCARy3KD5vuW2AZUAi4XkQ6isg4ETnnl2UYAdSqVcttvly5cjRq1Ii4uDgSEhI4d+4cL774IpUrV2bRokXs2LGDl156ibS0NPbs2UPdunUtF4yI5Y5xQbAKnxoAEcywYRQePdrpfYt69llKly6dC78y/1KsWDEKFiwIQJEiRYiJiaF///4kJiY6tzl9+jS///57wMeIjc1y6bJgDNx88x7at28f8HEURVEijaJFi9KjRw82bNjAZ599RvXq1albty4lS5bklltuYeTIkTzyyCN5rWboiI21PGmxsXmtSUD43PVpjJkAPCMiKeFVyXcC6fr0RlJSEj/++CMpKSk89thjFClSBIC///6bc+fO0aZNGz799FO6detGp06dvAvK3K/miqOPTfGbc+fOMWTIEFq2bEmXLl3o27cvHTt25MyZM9x1110By23WDDZutP43Bp57zrpEGzZsIDk5me3bt/Pwww+H6FcoiqJEFjabjagof3w2SjAE0vXpj6FWXESSAtIsTITSUAMYMGAAJUuW5Nlnn/W4fvXq1bRq1cp3ga5WgBppIWHt2rWMHz+enj17UrlyZZYtW8batWs5ffo099xzD5dddlnQx5g5cyYnT56kVKlS3HzzzSHQWlEURVHCbKhFIqE21EaNGsV9992nXZP5EBGhb9++vPXWWxw/fpyDBw/SokULv+WcOnWK7777jueeey70SiqKoiiXNGqoKZc0Bw8eZMiQIdSvX59z585RtGhRihUrRpEiRbj1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V67s9OBt5TnOrnFXnGVkZDg0niQyMpL27duTlpbGV199lWvfiRMnqFy5MlWqVHHoNY8fP86uXbs4fPgw119/PX/72994+OGHadCgASdOnKBbt27UqFHDfkdafi3XgYGBtGnThrFjx+ZbQPqjAwcOcP311wNwyy230KlTJ+bPn+/jrCw//vgjEyZMsF80rwZxcXG53puRkZEu331sjOG9997LVVjPnj27xE1S3qNHjzznD728UJs9ezaDBw9m8ODBTJw4kTlz5vDf//6XzMxMvv76a0aNGsULL7zAwIEDGT9+PIsXL2br1q1+cz54kxZqRcAYQ0ZGhlcWrr7mmms4ceKES8/dtGlTrgkJlfOy5wM6cOCAw2OziqsjR444PL1Mo0aNuOuuuwgJCWHdunX27fPnz+fBBx90+DVr167Njh077K0cYWFh9sJl0qRJlC1blgcffJCZM2eSnp6eq7C5XNu2benTpw8hISGMHz++yO7o++OPP4iPjyc1NZVJkybl6ro5evQo58+fz/e5v/zyS65WjBo1ahAXF+fNdO1yTsdwuaNHj3Ls2DEef/xxn1woPfkzOHr0qH2ur8vXrrz++utd7jI/deoUt956K1u2bAGsaT82bNjA6tWr3U+6GMnvxqCyZcvae4MWLVpE3bp1KV26NEFBQQwdOpR77rkHgH79+tG8eXOGDBlCxYoVCQsLo3fv3kRERNCtWzeioqKu+rFuRX7XZ0m0fv16brrpJq/Erly5skszuKelpfHTTz8hIjRq1MgLmZUMUVFRHDhwgDVr1pCens5bb73l65S8pqAiKD+dOnVi8uTJVK5cmaioKIKCghweqykihISE8Msvv9C3b98r9mcvxxYQEEDv3r2ZN28eoaGhtG3bNt+YpUuXpk2bNlx33XV8+OGH9OvXz6nvJy9xcXF8+umnXHvttTz22GO59i1fvpxjx44RGhrK/v376dSpE8OHD+epp56iatWqzJ07l7CwMDIyMjDG0L59exo0aMCECRPIysri2LFjV8SsVKkS586dIzIy0u3c85Oens6ECROoWrUqISEhlC5dmh49elCunDUv+eLFi3n22WcJCgoiIcGzU14uWrSIo0ePkp6eTkJCAi+88EKuuwYPHDjAhAkTGDt2rFNxV61axQ8//MDLL79sn24ie/uhQ4do2rQpf/75JzfeeKN9X5UqVVye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j7Jw4UJ69erFwoULr5h8siBr1qyhf//+ALRu3ZrSpUuzbt06Nm7cyKBBg3j66aftBWvOOZTAKnIGDBhQ4MSjzmjfvn2uxyJS4ADz7Avj5fNlAWzfvp3p06dTrlw5MjIyCpzR/69//SvffvstqampHD58mC5dutgvgD///DOxsbG0a9eOV199Nc88VcmSc94qT8p5kapevTq7du2yDynYs2cPERERnDx5Mte5XdCHyex9tWrV4osvvuCOO+5wOJcaNWqwcuVKHnnkkVzbnZlmpmfPnnzyySccOXKE9957L88uwTFjxtiHmDg6uXlkZCTnzp3DGIOI8Msvv7B161YiIyPdvlFo//79JCUlsWbNGq677jp7d3F+EhMTCQsLIyYmxiOFuj+pV6/eFd3pXbt25e233yYoKIghQ4bkuomudOnSDBo0iIkTJxIREUGHDh349ddfadiwIXFxcQwbNoxrr72WypUrs3r1atLS0ti7dy9Dhw7lzTffJCYmhoSEBP7v//6P+vXrM2HCBF588UVOnjzp8vCiYl2ogXVxuueee5g1axYLFy4kJSWFFi1akJKSwooVKyhdujTt27enYsWKeY4byG88RP/+/XPNdlyqVCm6du3KDTfcYJ8l/YEHHmDBggU8+uij7Nq1y6V10PxR5cqVOXbsGA899BCrV6+mZcuWXh27tHr1au69916+++479u3bR7ly5ejWrRtTp07Nc6LTnLZv386KFStyfSIXEZo0aWK/ODjSohgREeH2jQTe0KRJE37++WeCg4OvuNhc7tprr2Xu3LmICEOGDOGjjz5i+fLlANx4441ev/tYKbDm4Mue965KlSq5WsdXr15Nz5497eMpJ0+eXOgceaGhoYBVqG3evNmpHot7772Xjh07OvcNXCYsLIwBAwawZ88e5s2bR3JyMqmpqQQGBhITE0P16tVp0qQJd999N+PGjXP4jluwWpTGjh1Lhw4d2LlzJ3379mXmzJlXLGVVkD/++IMpU6bw7rvvAlahPG/ePAYPHkxKSgrTp08vtCjfvn17ob+Hq0n2ON86derkOdNBUFAQL730EmD9PN9//30aNmzIrFmzmDhxIlu3bqVly5b247OL7caNGxMQEEBWVhY//PADCxcupHnz5sydO5cDBw7Y38vOKvaFGlg/9EceeQRjDGlpaU6dKPkpW7bsFdMmZP8ismUP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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADNg0lEQVR4nOzdd3hUVfrA8e9JQu+9V2kiSJcmImtlXfVnWWUta0UUFXWt7FpQUUQsKL0j2FAsqIj03nsnhN4SIARCIKTO+/vjzgwzySSZmkzg/TzPPMncuffcMzP33nnvqUZEUEoppZRS4SeioDOglFJKKaU800BNKaWUUipMaaCmlFJKKRWmNFBTSimllApTGqgppZRSSoUpDdSUUkoppcJUvgdqxpjqxpjxxpi1LsuKG2OGG2P6G2MmGmOa5He+lFJKKaXCTUGUqF0LzACMy7IXgUMiMgj4HJhQAPlSSimllAor+R6oich0ICnL4tuAlfbXtwKtjDFl8ztvSimllFLhJFzaqFXFPXg7a1+mlFJKKXXZiiroDNidAMq4PC9rX5aNMeYp4CmAUqVKtWvWrFnoc6eUUkopFaD169fHi0gVX7YJl0BtJtAZWGqMaQlsFpGznlYUkbHAWID27dvLunXr8i+XSimllFJ+MsYc9HWbguj12R14GKhhjHnTGFMC+AKoZ4x5E3gZeCK/86WUUkopFW7yvURNRBYDiz289Gx+50UppZRSKpyFS2cCpZRSSimVhQZqSimllFJhSgM1pZRSSqkwpYFamJs3bx7XX399QWdDKaWUUgXgkg7Uli1bRo0aNZgzZ45z2d69e+natSvvvPMOAO+++y6jRo1i1KhRPPjgg27bX3PNNfz+++/Z0j1+/Dh33HEHffr0YcyYMTz00EMcP36c119/neuvv57x48czfvx4HnnkEbftHK+PGzeOfv36MW3atDzfw4033uj8f/LkyezZs8fjegcOHODRRx91Pn/zzTfzTNuTI0eOcMcdd3Dbbbc5l4kIHTt2pE+fPpw7d87rtIYMGUJiYiKABptKKaWUH8JlHLWQuPbaaylXrhw333yzc9kVV1xB48aN6dmzJwC//PILCxcupEKFCnTr1s253sKFC3nooYf47LPPuP32293SrVatGm3btqVZs2b06tWLffv2sXr1anr27EliYiJPPvkkH330EV999ZXbdo7X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12AqryoXm83GzJkzAWsQZUcprSNQW716NWvXrvUrbydPnqROnTqcO3cOgKVLl7Jp0yaP686ePZvVq1f7tR+lgunYsWMelycnJ+faVOT1119nx44dVKhQgY0bNzqXHzp0iL59+/LFF1+wcuXKoOc3N6tWrXL+/9dff7F79+583b9SgbqkArXWrVtz8uRJMjIyKFasGHv37iUi4uJbbNiwIS1btnTbpmLFil6VqHlaZ926dXTs2NH5vEqVKlSsWJHy5csHXF2mvLdo0SI2bNjgcfn69esZPnw4v/32W7bXT58+zahRo9i4cSO//PILYAVq9erVwxhDeno61atX588//2TRokVuJQmOwCsvu3bt4qabbuLIkSOA1V4tp3H2Lly44DbMzKFDh7zaRzD5G5CqS8ubb77p8ZpXuXLlHI/f+Ph4YmJi2LdvHw8++CBbt251vnbw4EFatGjBq6++mm22j9TU1GxpBXOcy9dff93Z0ez06dPs2bMnaGkrlR8uqUCtdu3a/Pe//wWgRo0a7Nq1i7Jlyzpfv/XWW7nmmmuybRcVFZXrhWHy5Mn069cPESEuLo7KlSsDsHr1arp27epcr27dujRo0IA2bdq43U16Y9myZdoZwUu///672/MLFy6wf//+bJ/frl27WLduHbVq1XKbYWDx4sUsWbKEr7/+mmbNmjF8+HDat29Peno6derUoUGDBhQvXpwLFy5Qrlw5RowYQffu3Z0Nqffv388bb7zhVV5jYmLo0aOHM1CLiIjIMYg3xjh/UDIzM3nsscc4ffq0dx9KkPz666/5uj8VnuLj4z3OylGlShVOnjzJTz/9REZGhtvxuXnzZrp168apU6coW7YsFy5cYMiQIYgICQkJVKhQgbJly7pV/dtsNu6//35EhOTkZGw2G0C2qlOwziVfJSYmcs011zhL1UqUKMHx48d9TkepghRQoGaMqWCMqWt/DAhSngLJD/Xq1XP+v2PHDq68Mu/RQho0aOCxZ97OnTv5+OOPadq0KXfccQcxMTHMnz+fG264wW2fDrVq1aJz5840atTI57u2H374wefg7nI1btw4RMRZPRMVFUVmZiYvvfRStrvzkydPUrFiRaKiLs6WFh0dzY4dO4iKiqJHjx7cdtttdOzYkYiICHr16kW9evWoXbs2R44ccR5TLVq0YNu2bYgIP/zwg1fHFVhj7jVo0MAZqEVGRjp/jDxxHE+HDh2iX79+TJs2Lds6KSkpfjfqzsvevXtDkq4qPNLS0mjbti1bt27lu+++c1ZVighVqlTh+PHjfP7558yfP9+to8yePXu488472bXrYsf/BQsWOAMjYwzGGLcbqkOHDtGlSxfmzp3LF198wbZt2wBYsWIFIuJ8PmnSJH799Ve2b9/uNih1XqKjo+nVqxdbtmxx5sFh3Lhxvn40ShUIvwM1Y8wEYCkwGfgKeDhIeQqY40KQlJRE06ZN81zfMfdnVgsWLOCVV16hc+fOdO3alRUrVpCYmEj58uWJioqiXLlyHtOLiIhw/hinpKR4VZ3UrFkzt7YUyuKp8fGFCxeIj4/nlVdecS6z2WxERUWxYMECt3UzMjK45pprKFasGCkpKc51MzIynNXi9957L61ataJXr17OZXXr1nWreixZsiQXLlxgypQp3HXXXURGRuaY56zTmRUvXty5b4cpU6bw559/Ztu2dOnSnD9/nujoaFq1auVxbMDt27ezePHiHPfvsG7dujzXyWr//mzDI6rLzJ49e+jRowezZs2iSpUqzmYFiYmJ1K9fny1bttCqVSsWLFhAr169GDJkCCkpKWRkZNC8eXNq1KgBWNff9u3b51oStm3bNh544AG2bt1KSkoKMTExpKenk5CQQFxcHBMmTCAuLo6oqCheeeUVxo0bx9ixYz2m5Wlsy927d9O0aVO3AM3x+zB58mSdRUYVCoGUqJUTkRYi8jcR6QE8EaxMBSolJYWSJUvy73//mxIlSuS5vqP0xJXjZHb8cJcsWZLExESaN28OWB0RevTokWOaju2/+uorli1blmceXNvSqYs++eQTt+cJCQm0bt2a1atXk5iY6AxkTpw4wf3338++ffsAq1SgSJEivPLKK9SvX5+6deu6VeUcP37cLYiPiIigYsWKzue1a9fO1gkhJSWFCxcu0KRJEypWrOixCjM5OZkhQ4YAcP78eeewLTabzXlMOKp5HIHgwYMHnUPE1KxZk9jYWA4dOkTdunWpUaNGtobd27Zto1GjRnl+dhMnTvSqmmfdunXOm4T09HStgr9E5TRvbdamBDt27KB58+ZERERwww03EBERQWZmJqdOnaJKlSpER0fz5JNPUqZMGVq1asU///lP/vzzTyIjIylWrBgDBgwArJvPJ554gr179+Y4GPmRI0eoVasW//jHP/i///s/4uPjOXbsGM2bN2fjxo2kpaXx559/cu+992KMYejQoVSvXt1jWt9++63z/zlz5nDmzBnnoNWO9+CQnJxM+/btWb9+vS8foSoELly4QJ8+fQo6G0EVSHSw3RhT2uV5hUAzEyylS5emZs2a3HnnnV6tHxERke3HaePGjbRt29Zt2QsvvMD1118PQIcOHahVq1aOadaqVYuDBw9is9nyHGPt1KlTVKxY0WM+LndZSxl37txJz549mT17Nvfddx+zZ8+mfPnyPPTQQ7Rr187ZzmvevHl069aN0qVLY4yhbt26HDx4kDNnzgDQs2dP2rVrl+N+S5Qoke37LVWqFHfddReQcynssWPHnPtYtWqVs01kkSJFWLx4MVdddZVzXUc10OzZs53VOTVr1uTYsWPYbDYiIiK46aabmDt3LgDjx4/n2LFjJCcn07Rp02w3F1lVq1aN5cuX57oOWDNtbNq0idTUVMqXL5+t9E9dGgYOHJht2cmTJxkzZozbsuPHj1OlShW+/PJLjDF06NCB9evXEx8fT+XKlTl27BgtW7bkzTffBKB+/fr8+OOP/O1vfwOsYx3gH//4B/Xr12fr1q00adLEbR/r1q3j888/5/DhwxhjaNq0KW3atAGsG5cuXbqwZMkSGjZsSEpKSrYbbhFxO05TU1Pdqlznzp3LihUrnMHZlVdeyc6dOwEoW7YsmzZt4u6779bmJpeY999/nwULFtChQwePHcwKq0ACtceAE8aY/caY/UCBz/vp0KhRI+rXr+/TNiLiNr7OmjVrPHY88Fa3bt0YNGgQ1157bZ7rxsTE0LRpU2dpSmpqKi+++CJLly71e/+F2aFDh9wuwq6Nj3fv3k2XLl1YtWoV//znP9m7dy+33347jRo1IiIiwllFHR0d7fbjUKdOHZYsWcKHH35Iq1at6NKlS57zeGZth9a7d2/nUC+NGzf22M3fURowatQojh075iy1u/POO/nxxx/p1q2bc92KFSty+vRpZ+kfXAzUHAF7mTJlOHfuHBs2bKB27dp8//33xMfHc+211zqrelasWOEx/9WrV/d6NHgR4eTJkzRs2JBz5875VW2a32bPnl3QWcgXWavSfTV37lzS09M9Dv3y119/uR2Tnlx55ZVER0dz6tQpKlWqxNVXX+3W5hOgf//+2YIxh3379rntwxjDihUreOmll3jmmWeyre9otxYTE0Pjxo2z9bA2xrBx40YmTJgAWCUoR48epWrVqoBVStelSxd27tzpvElu27at84e7S5cuTJ06lcaNG+dY0pf1hllnVSgczp07x/jx43niiSf8niIyHAUSqH0rIiVFpIGINMCanSAsXH/99VxxxRU+b9e/f39n6YVjBgN/VaxYkVOnTtGiRQuPY6udPn3aeTHYtm0bjRs3plmzZuzatYuYmBiuu+66Ah8csqDMmDGDzZs3k5qaSosWLdxKrtLS0ihevDi1atWiZMmSPP/8824llldffTUzZ87kjjvucEuzWLFiLFq0iIEDB9K5c2ev8vHCCy/k+Frp0qU9DtFx7Ngx7r//fsqUKcPDDz/srNKuWrUqI0aMAKwflhIlSlC3bl127Njh1taxbNmynDlzxu2YKV26NMuXL+fWW2/lpZde4oknnqBChQqcOnUKEWHKlCk5lsTmVUK7evVqmjVrhjGGuLg4GjZsSFJSEl9++aXH9UeOHJlrevlp6tSpBZ0Fv9hsNq968+7evZtz5845S64cfC11nzZtGkuWLHHeZLhKSkqiWrVqpKSksGbNGt577z3q1q3rtk6pUqVITk4mPj6eSpUq8dRTT2VL5+qrr85x/wMHDnSOTQjWueiouq9du7bbujabjYSEBBo3bszp06fp2LEjN910k9s6VatWZf78+Vy4cIHFixfz1FNPsW/fPurVq0dmZqazN/6OHTuczVNKlSrF+fPnAahXrx4bN26kRo0a2XqEg1Ut+vbbb7st83Z2kQ0bNlxSJTmFQVxcHAcPHkREaNCgAW+99Zaz48qlwu9ATUTeMMaUs8/3WdY+tVShVbp0ae6991527txJWlqaV23b8jJp0iSMMR6H//joo48YNWoUmZmZzrZMDRo0YO/evURHR9O5c+fLdiw2m83Gzp07OXXqFF26dCE6Oprp06ezaNEi5zq9e/f2uK0xho8++shjoP7ee++5DXicF9ehXTzx9IPpaMf40EMP5bhdamoqDRo0oG7duvzxxx9cffXVpKenU6RIEYwxHDx40Nl7GeDhhx/mueeeA6z3V7NmTQCuuuoqYmJiOHLkCPHx8aSlpTFv3jzndo4qX0eP5lOnTjnnMwWrNGXbtm387W9/o23btvz+++9cccUVnD592llN5CojI8OrqtT8cujQIa/Hswsn0dHRfP3118ye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nDlKAFJSUpzVklu3buXqq692ttPq1q0bP/30E9dccw2bN2/mqaeeon379tn2XbZsWWdVoGP8vbi4OGrWrMm6deu49tprOXDggLPK3HHeuFblRUREcNttt+U5HIcnXbp0oUWLFj5vl9+eeOIJn6vnK1euTP369bn//vu58cYbAdwGtI6NjaV69erOIMLTsD7JycnOXt2NGjXitttuy3GAYZWz7du3M3HiRO68806OHz/O4sWLeeSRR4iPj0dEiI6OJiUlhV27drF//37atGmTbdpAh8jISDIzMzHGOMcodZwXNpuNNWvW0K1bt1x7YO/Zs4eGDRs625yuW7cu3+dfhgBK1ETkHPBfx3NjTCcgkDmQTgCuZ0BZ+7Ks+x0LjAVo37695DZoabjJyMhg0qRJVK9ena5du1K9enWMMTkOvLp+/fpsr61evZrIyEi35Y7xjTIzMxk1ahRbtmzJcZqVzMxM9u3bl+tgr1l50zZMREhNTXU2RN68eTOtWrVi27Zt2Gw2ypUrR7du3RAR3nvvPYYPH061atW48847+fnnn+nRo4dzMFmH+++/36d8FoSmTZsyadIkevXq5fzBzk9t2rThpptu4q677iIqKopmzZrRuXNnZ7CfVcuWLfn73/+e68wGUVFRdO3albi4OBISEpzfwd69e2nTpg3lypUr0B/t8+fP06xZM9q1a8fatWtZv349xYsXz/FYOXHiBBEREdnaHvlq6dKl3HHHHSxdupSiRYvyf//3f2zdupV69erRrl07FixYwF9//UWfPn2cPc82bdrEggULKF68OOXLlyctLY17772XdevWcf78ea6//nquuuoq6tevz/Tp071qmwjWYMqRkZE0bdqUOXPmcM011/DTTz/x9NNPOwNEx9R1roFfuJ9P4cLR1qxt27asXbuW66+/nujoaOrUqUONGjWcx57j81y8eDEVKlSgXr169O7dm8WLF1OtWrUcz0Pl2aZNmxg/fjzFihXjyJEjZGRk8Nhjj3HPPffw+++/s2rVKmJiYnjkkUfYt28f7777Ljt27MjxuP7tt9/o2rUrycnJVKpUiebNm2Oz2di0aRNvvPEGcXFxREZGUq1aNY8D2K9Zs4ZmzZpRqlQpmjdvTsuWLSlatKgzjZxGHwg2n0vUjDHP2v9OdH0AwwPMy0qgnjHG0RCiKzAzwDTDSlRUlFv3+AYN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W7ZsyTZ80RtvvEGlSpWcgyWXL1+eY8eOOQs93n77bc6fP8/EiROJj4/nwIEDzg4mtWvXZu/evR5nfPGGP23UutsHo73eGPO2MeZt4EWgjl85UHlq0qQJP//8c7YG3AkJCezatYtatWqRlpbm/CGpUqWKW7H7qVOnSEpKck4zVKNGDVasWMG8efO45557WLduHT/99BMXLlxwKz1wzHdqjOHIkSMcOnSI2NhYqlWr5pzk/P7773c2bAarVM1RJF2sWDFnNVvWEghX3s69qfLHPffck+vrjkntwerM4Fp96bBp0yY6dOhAq1atSE5OdqvC2LlzJ3feeSfR0dG0b9/e2ei9UaNGLF26lGHDhnHmzBkqVKjAFVdcwZYtW+jYsSOzZs3Ktf1dREQE//rXv5y9mrPeva9YsYInnniCPXv25JhGWloac+fOZcWKFYwePZpt27bRsWPHXHvwVapU6bKd7u1S5rjxdHDUVGR93TGAscpZeno611xzDRs2bOC3337jxhtvZM6cOZw6dYqqVasSERHBjh07gjrQfFb/+9//PC53nZKtaNGipKens3r16jxLyBs1asQbb7zhbAa0YMEC9u/fz2uvvca4ceOYNm0aZcuW5fz584gIDzzwgHPmGF/5U6J2BjgAJAIH7Y89QM4zWKuAlC9fngULFmQLaGrXrs369etp1aqV2/gxlStX5siRIxQvXpzixYvz3Xff8euvvzJ16lSefvppatSowddff83jjz/uLK2Li4tzDq7pKLI/ceIEVatWpXz58uzcuZMHHniAYcOG0alTJ+rUqcPixYv5xz/+wZYtW5zjut16663ONjm33nprrpM1q0uD4852x44dzlKEli1b0qxZM6Kionj99deBiz9sjul/0tPTueqqq5yBXt26dZk2bRq33Xab8wexUaNGLF++nM6dOzNlypQ8h5aoX78+V111FR9//DFjxoxxm0R+586dNG/ePFvJ7oQJE5wjlO/fv5+xY8eyevVqbr75ZoYMGcLNN9/M9u3bc6yer1ixoo6XdYly3KxC9htgsI79cJqxI1zt27ePJk2aEBkZyd69e2nZsqXbrCIVKlRgz549zim+QiGnzjpZS9pEhJSUlDxL7Jo1a0bv3r1JTk4mKSmJOnXqsGzZMpo1a0avXr345z//Sd26ddmxYwdly5YNqE2qP23UNovIV8BTIvKV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fUefPn0oUaIEHTp0YMWKFRhjHO56PnLkCC1btiQuLo6LFy8ye/Zsxo8f73Zz/7Zt21i4cCEvvfQSt912G2B9joYOHUqVKlUQEYYOHerWazirVKlSTs1a3q5dO6fHgkZERNCrVy/Kli3LgAEDaN68OWXKlGHOnDmcOXOGmTNn2udpy8jIYNGiRSxevJi+fftSv359n602Ur58eaZPn87ly5eZO3cuZ86cISUlhYceeojRo0df1Qr066+/MmrUKCZPnszXX39t379161YGDBhw1d+LBx54INfXze38yCrUcpPffHq52bNnD/Xr189zf9b5VatWLaKioujYsSMiQpcuXfjiiy84ffo0f//731m8eDEVKlTg999/B2Dt2rX2z3XTpk3Zvn07s2fPZuDAgV4ZlhIcHEytWrXy3O/p7mtvuHDhgtfH0l28eNErXavh4eHUr1+f+fPnF4mWy4JooVaA9PT0XCfgW7p0KUeOHLlqe2pqKuPHj2f06NGUL1+eChUqFEKWOYWHh3Pw4EG++uor1qxZQ0RERIEXlPr163tkvI4vZWZmMnLkSH744Ydc93/55ZdMnjyZb7/9losXLzJz5kw6d+5sn0Rx+/btNGrUyK0cqlWrxp9//klsbCyVKlXiqaeesheCWWtPZhUfgYGBBAYGsnr1aoYNG+bwexQROnbsyIwZM2jXrh19+vQhNDTUpZtKwBrM+8MPP9CrV69iPSmziFCzZk3atGlDpUqV+O6772jfvj3Tp09n4sSJDB8+nEaNGvHKK694rBXAVY888gi7du3iscceo0ePHtx+++20adOGihUr0rt3bz766COmTJnC6tWrSUxMZOXKlQwaNIiXXnqJ1q1b8+mnn9ovYBEREZw+fZpTp06RkZFBfHy8faC2I66cZgGs2egnTpxI9+7dC7xQJiYm8uabb/LJJ58we/Zs6tatm+tx69ev5+LFi5QtWxawCvOsFqno6GgaNmxIx44dCQoK4vjx43Tt2pW9e/cC1mc8a0xdkyZN2LRpExkZGbm2HLorOTm5wBbNChUq+O20SsYY0tLSCuUzfuHCBZo1a+aV2O3ateP6669n5cqVAFy6dInp06fnuAs9uzNnzvD999/nO+Guz2T13RfF/5o3b268ITMz0/7vMWPGmHfeecfEx8fnOGb48OFm+vTpVz133Lhx5uzZs17Jy1lnz541W7ZsyfF+8rN27VozYsQI89tvv3k5s6tlZGS4HWPmzJnm8OHDZvz48Tne86RJk8z27dvNwoULjTHW7+i9994zSUlJxhhj1q9fb7Zs2WImTZrkdg7nzp0zU6ZMMRMnTjQJCQnGGGOWLFliPv/8czNx4kQTGxub4/jff//ddOvWzcyfP9/s2bPHrF+/3hw+fDjX2ElJSebTTz/NdV9GRoYZO3asw3muXr3apKenm4SEBPPhhx+aU6dOOfxcVXRs377djBw50ly8eDHH9mXLlpnPP//crFq1yhhjzOXLl820adPMRx99lOfnLz9TpkzJ8fizzz4zSUlJZu7cuebMmTM59l15rq9cudLs27cv39jz5883r7zyivn999/tOecnLi7OpKenm8mTJ5tTp06Z2bNn59jfpUsX8+effxYYJ+v1nbF+/Xqzbdu2Ao+bNm2aSUxMdCq2Ny1ZssSMHz/ezJw50/Tt29ds3LjR66/53//+16Snp3v1NT788EPz+++/mw8++MAkJCSYFStW2K8FxhiTmppqli5dakaMGGF27NhhPvjgA3PhwgVjjPVZdfR66Og1DIgxTtY6RbpFLT4+3t607ajMzEwuX75sr56TkpJITU1lzZo1zJgxg/Hjx/Paa69x4sQJNm3aRLNmzRg6dCjjxo1j9erVxMfHM3z4cNq2bXvV0jXbtm2jdu3aXr1t3hmRkZE0a9bM4e6ZW2+9lcGDB7NkyRL7nFSmkJqN+/fvz7x58zh//jxjx461fwty1JYtWyhTpgzR0dG0atWKkSNHsnLlSmJjYwkJCWHYsGH2QbFPPPEEL730kn2tultuuYXFixd75L1GRERQq1YtbrrpJvu39QceeIBLly7x8ssvXzVDd506dRg2bBiPPfaY/SaBHTt28PHHHzNx4kRWrVpl/4a3bNmyPLukAgICCA0NtXc15fZejDH89NNPjB49mvT0dD766CMWLlzIww8/7JOWX+V9jRs3ZtCgQVd1L91///0kJydz++23A9b4rB49etCvXz+io6Pdft3k5GRKlSplb2HObsCAATm27d+/P99uQrAG5999993s2rUr11nur3TdddfZW65XrFhx1ZjDkSNH2sfwFkRESEpKKvDvQ1paGsYYdu3alWOKj7x07NjRq3PQ/ec//2H79u0OH//nn3/y4IMPEhQUxNixY2nVqpXXcsty1113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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "text/plain": " index V model GridEx GridREx CART \\\n0 2016-03-04 00:00:00 410.0 518.750997 456.38289 441.865819 441.865819 \n1 2016-03-04 01:00:00 400.0 522.352090 456.38289 441.865819 441.865819 \n2 2016-03-04 02:00:00 395.0 525.129143 456.38289 441.865819 441.865819 \n3 2016-03-04 03:00:00 408.0 528.767307 456.38289 441.865819 441.865819 \n4 2016-03-04 04:00:00 406.0 528.091871 456.38289 441.865819 441.865819 \n.. ... ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 456.38289 441.865819 441.865819 \n644 2016-03-30 20:00:00 501.0 489.614882 456.38289 441.865819 441.865819 \n645 2016-03-30 21:00:00 518.0 490.609540 456.38289 441.865819 441.865819 \n646 2016-03-30 22:00:00 510.0 491.321955 456.38289 441.865819 441.865819 \n647 2016-03-30 23:00:00 511.0 491.970557 456.38289 441.865819 441.865819 \n\n COSMiK CReEPy \n0 462.073299 477.108816 \n1 460.069181 473.640591 \n2 457.718672 469.171813 \n3 455.242336 465.418572 \n4 454.052186 462.959830 \n.. ... ... \n643 451.642998 432.796454 \n644 452.503446 433.519832 \n645 452.375362 432.264649 \n646 452.801142 431.704527 \n647 454.179640 433.203885 \n\n[648 rows x 8 columns]", + "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.750997456.38289441.865819441.865819462.073299477.108816
12016-03-04 01:00:00400.0522.352090456.38289441.865819441.865819460.069181473.640591
22016-03-04 02:00:00395.0525.129143456.38289441.865819441.865819457.718672469.171813
32016-03-04 03:00:00408.0528.767307456.38289441.865819441.865819455.242336465.418572
42016-03-04 04:00:00406.0528.091871456.38289441.865819441.865819454.052186462.959830
...........................
6432016-03-30 19:00:00497.0487.521085456.38289441.865819441.865819451.642998432.796454
6442016-03-30 20:00:00501.0489.614882456.38289441.865819441.865819452.503446433.519832
6452016-03-30 21:00:00518.0490.609540456.38289441.865819441.865819452.375362432.264649
6462016-03-30 22:00:00510.0491.321955456.38289441.865819441.865819452.801142431.704527
6472016-03-30 23:00:00511.0491.970557456.38289441.865819441.865819454.179640433.203885
\n

648 rows × 8 columns

\n
" + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ "p = pd.DataFrame(predicted)\n", "for e in extractors:\n", - " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())" + " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())\n", + "\n", + "plot(2491, p['index'], p.model, p.COSMiK)\n", + "plot(2491, p['index'], p.model, p.CReEPy)\n", + "plot(2491, p['index'], p.model, p.GridEx)\n", + "\n", + "p" ], "metadata": { "collapsed": false, @@ -326,30 +290,12 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 39, "outputs": [], "source": [ - "def plot(testB, x, pred, extracted):\n", - " missing = pd.read_csv(\"data/missing.csv\", parse_dates = [0], index_col = 0)\n", - " df = pd.read_csv(\"data/averaged1.csv\", parse_dates = [0], index_col = 0)\n", - " df.LPFnorm[missing.index] = np.nan\n", - "\n", - " for i, b in bartels.iterrows():\n", - " if b.n != testB:\n", - " continue\n", - " xminmax = [b.t0, b.t1]\n", - "\n", - " f, axes = plt.subplots(3, figsize=(10, 8)) # l x h\n", - "\n", - " myplot(axes[0], [3, 1], \"LPF\", df.index, df.LPFnorm, xminmax)\n", - " myplot(axes[0], [3, 1], \"\", missing.index, missing.LPF0, xminmax, color='r', marker='*', lw=0, size=10)\n", - " myplot(axes[1], [3, 2], \"V\", df.index, df.V, xminmax)\n", - " myplot(axes[1], [3, 2], \"\", x, pred, xminmax, color='b', marker='.', lw=0)\n", - " myplot(axes[1], [3, 2], \"\", x, extracted, xminmax, color='r', marker='.', lw=0)\n", - " myplot(axes[2], [3, 3], \"B\", df.index, df.B, xminmax)\n", - " plt.subplots_adjust(hspace=0.6)\n", - " plt.savefig(f\"plot/{b.n}.jpg\", dpi=96 * 2)\n", - " plt.show()" + "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", + "pd.DataFrame(rules).to_csv('results/rules1.csv')\n", + "pd.DataFrame(missed).to_csv('results/missed1.csv')" ], "metadata": { "collapsed": false, @@ -360,22 +306,9 @@ }, { "cell_type": "code", - "execution_count": 37, - "outputs": [ - { - "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmUAAAHoCAYAAAAISZi8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAC8/klEQVR4nOzdd3iTVfvA8e/pYG8om7JEioAI4kYRwYELnOB4X30dIODg97r1VUFF2SAbRGQoLlQEFFRky95lbyi0tEDLKB20zf37I8MkTdukTdoU7s915Wr7JDnPSZrnyf2ccR8jIiillFJKqaIVUtQVUEoppZRSGpQppZRSSgUFDcqUUkoppYKABmVKKaWUUkFAgzKllFJKqSCgQZlSSimlVBAokqDMGBNmjHnXGDOpKPavlFJKKRVsiqqlrCywwL5/Y0w3Y8yLxphBxpjSRVQnpZRSSqkiUyRBmYicAU45bXpcRMYA64EHiqJOSimllFJFKayoK2BTyvbzBHC9+53GmB5AD4CyZcteHRUVVYhVU0oppZTKnw0bNpwUkQhvHhssQVma7WcEcMT9ThGZBEwCaNu2raxfv74Qq6aUUkoplT/GmMPePraoBvoboBvQ1BjTBphpjHkRaAv8XBR1UkoppZQqSkXSUibWVdAH2W4AG4uiHkoppZRSwULzlCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDMqWUUkqpIKBBWRBauHAht956a1FXQymllFKF6KILylasWEGtWrX4448/HNv279/PTTfdxAcffABA//79GT9+POPHj+eJJ55wef61117L3Llzs5UbHx/P/fffT8+ePZk4cSJPPvkk8fHxvPnmm9x6661MnjyZyZMn89RTT7k8z37/559/zssvv8x3332X52vo1KmT4/epU6eyb98+j487dOgQTz/9tOPv//3vf3mW7cnRo0e5//77ueeeexzbRITrrruOnj17kpyc7HVZQ4YM4cyZMwAaWCqllFI+CJbksX7Trl07KlasyB133OHY1rhxY5o0aULnzp0B+Pnnn1m8eDGVK1fm5ptvdjxu8eLFPPnkkwwfPpz77rvPpdwaNWrQpk0boqKi6N69OwcOHGDNmjV07tyZM2fO8NxzzzFw4ECmTZvm8jz7/c8//zxbt27l008/pVu3bkyfPp2UlBROnTpFhw4duPHGG/nkk08QEWrXrg3AhQsXWLZsGQD169fntddeo02bNuzevZtPPvmEZcuWsXfvXqZMmcJ1113HDz/8wMcff8z27duZPn06TZo04ejRo7z11lt0796diIgIIiMj2b9/P1OnTnXUsW7durRp04Zjx44xf/58OnfuzHfffUdUVBQdOnSgXLlyjBkzBmMM8fHxdOzYkZIlS9K9e3fee+89Fi1axKOPPsrNN9/M8uXLueaaawgJCSEuLo7Jkyfz+OOPM2TIEOrWrcvu3bt56qmniImJoW/fvvTt25cpU6YwfPhwFixYQPXq1SldujTPP/+8Xz8XSimlVLC76IIyb0yYMIHXX3+dpKQkOnToQIsWLQBYsGABgwYNYt68eWzYsIHWrVvz8ssvExoaymeffQbA0qVLSU5Opnr16txzzz0sX76c6OhoRo4cyd69ez3ub/fu3YwbN45ff/2Vzz//HIAmTZqwcuVKSpYsyfTp02nZsiXz5s1j5cqVJCcnM23aNEqUKMEtt9ziKKdu3bpYLBaWLVvG8ePHueWWW1i0aBHPPPMMALVq1QLgo48+on///jRt2pRu3bpx6NAhunbtyrlz53jppZdo3769x3oOGDCALl26cMMNN3Dy5EkaNmwIwPnz5/n2229ZsWIFKSkpdOzYkVWrVtGgQQNHMDZkyBC6dOlCmzZtALjllluoVasWzz33HLt27WLXrl188MEH7Ny5k/79+/Ptt99Srlw5evbsydNPP82ff/7J/v37uf3222nZsmVB/8VKKaVUsXPRB2V79+6lQYMG2bZPnjwZEeHOO++kXbt2lCxZkpMnTzJ16lSaN2/O0KFD+eabbxgzZozL89q3b0/37t1dtrVs2ZK+ffvmWIemTZvSu3dvUlNTmTt3Lj179qRXr16sXbuWmJgYPvroozxfx7x58zh9+jSvv/46ixYtIi0tDevCCGCxWBy/21nz8+KyvXz58tm2OatevToPP/wwjz76KD/99BNDhw51lOWpPHuZp06dIiMjI1t5xhhEBIvF4vH55cqVwxhDqVKluO6667jmmmuYOnUqX3/9NZMmTcrzPVFKKaUuJhddULZixQpOnz7tCChWr17N22+/zd69e5k/fz7XX389kydPZsuWLWRmZlKvXj1q1KjBiy++yNtvv03btm3ZtWsXN998M7Nnz6Zr166AdUzZxo0biYuLo0OHDtSoUQOA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VWP58uXExsbSqFEjJkyYAMALL7xA9+7dqVevHjExMaxbt462bdtSvnx5Nm7cSOXKlXOsT3h4uMtA9uHDh3PkyBE+/vhjwNr1+ddff2GMYd++fTRu3NhRxpo1a1iyZAl79+71ODjXLj09nbZt27J582bHtvj4eE6ePElERAQXLlygQ4cOrFixgg8//JCff/6Zl156iaVLl3rzFjscOHCAwYMH06ZNG+rWrUv58uXZv38/1atXp1q1amRlZXHnnXcycuRIoqOjueqqq8jKysIYQ2pqquNE/9prr9GgQQNH92/16tWpW7cuMTEx7Nmzh6eeeoqjR4+SkpLC7NmzHf9D5/fX/tpat27NSy+9xHPPPUeDBg14+umnadWqFU899ZTj8RqYqcJ2/vx5ypYtS506dYiPj6ds2bKcP38esI7BjI+P93qc5759+1iyZAmZmZmEh4fzyiuvEBERQb169TzmJqtWrZrLONvy5ctzzTXXuGTwr1WrFnXr1nV5Xtu2bbn22msdf4uIy4XeyZMnqVatWp71HTFiBN988w0nTpzIcViFunT4kjw2S0SGicijInK3iHQRkR4iEpgRmcrvTp48SdWqVTHGsGHDBgDHiU9EWLFihSP/zr333kuPHj3IyspyBGbdu3fn/vvvp0+fPpQsWZL27duzY8cOjh07Ru3atR2Pq1SpEqGhoVSsWJGzZ886lijp1q0bGzZs4KGHHnLUyVMAULduXbZs2UJqaio1a9Zk9+7dpKWlERcXx7x589i6dSvGGNLT02ncuLHjeWfPniUzM5P69etn6wJNSUlxnDCTkpK4/fbbXVrf3n//fU6cOEHjxo05e/YsJUqU4NixY3Tt2pXx48fz0EMPsWHDBo+zTHOydu1a3njjDa6++mrAetW8bt06atSoQadOnbjnnnuIioqiXbt2zJkzh1atWjlaCa+77joee+wxAK655hrHe2tXr149jh49yv79+2nUqBFVq1bllVde4c8//6RWrVrAPwGWMYY9e/Zw+eWXO7aHhITQtWtX6tSpA8Cdd94J/DOrTKnCdOTIESIjI6lXrx41a9akWbNm7Nq1y3E8v/766yxfvjzPcpKSkrjttttISEigWbNmLvd17NiR48ePZ3uOMYbjx487WuiefPJJbrnlFp9fw80338yyZcscf69du5arr77a44Xwzp07uXDhAgsWLOC2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7sS9td2+7qKdPZs8WINvzX9mVadOnTwnaNh7Bt5//31q1KjBiRMnaNasGVdcccUlEZAB+Q7IwDrm1fn7olatWvTv358rr7yShx9+mCVLlrg8fuLEiS7dv8ePHy/UtWt//vnnoDw+pk+f7rI4fKDs378/x+Xr8puE29fuy8+Aj0TkGRH5D/BOvvaa3U/ANcaYF4Gvc3vguXPnHCdMsLYaLV26FLCeXDMyMkhJSSE6OprGjRvTrFkz4uLiWLNmDSNGjHAMVhURIiIiOHnyJG+++SZHjx51yfnl7ueff+b111/n6aefZvr06Zw8edLxhWSf/eisZMmSpKenk5qa6tXyNnYlSpTgyJEj2bo3nV122WUuubvat29/yZzwgkmJEiUuyZmt0dHRXH755SQlJVGhQgVKlixJ5cqVeeSRRwDrlbx9hvCUKVP473//63hurVq1qF69OqGhoaSkpNC/f38sFgubNm3KFizZzZ8/n99++41GjRpx8OBBbrrpJubPn++yDundd9+d7Xn2hemdvzRmzJjBpk2bXCYL2FfjsAd0VatWdYz1XLZsWbbALticP3+ed955h4ULF2b70vbGokWLHF8g9nOps2PHjpGZmUmdOnU4fPgwv/76K3FxcR7LsndfhoSEONZn7dChA3fccYfP9boUlStXzmX8WHh4uKNl0hjDtddey88//8xnn32GiFCxYkWXC5Tjx487xnYWhjlz5uT4WShKxpiA5EJ0FxsbS8OGDbNNzvv222/p379/vsr0KSgT61nrGqdNnfO11+zlpojIGyIyJrdB/mC9Snc+cRw5coRTp06xePFihgwZwssvv8zOnTtJTk7mueeeo2vXrlgsFqpXr86YMWMIDw93ZGS3n3yvvfZaXnrpJTZt2sT69euzLW1RtWpVYmJiKFOmDBUqVKB69eps3ryZFi1aULNmTb+3Uh07dizHNdrAejV1//33O/5+++23/bp/pdyFh4ezfft2PvjgA/766y86dOhAQkICkZGR1K5dm//85z+O4yAqKoro6GjHBUnZsmUd5TRp0oTWrVvz8MMP06tXL1q2bMl3333n6NJ0t2HDBi5cuMD3339PZGQkVatWpUyZMowePZodO3Y4voBeeuklwDq+zDnlyUMPPcTMmTMB6/iPunXrcvDgQaZOncqJEydc9pWYmEjVqlVdgrKTJ0+6rKIRjObMmUPVqlVZvnw5+/btyzG4zckPP/zA4cOHSUpKYtCgQdnunzp1qqOlKzMzk61btzouYNPT03n77bcZN24cp06d4sKFC47W41q1arkkelV5a9euXa4tbTfffDNJSUncfvvtDB06lI4dOzq+z+bMmeNYnsku0AFThQoVimQMW2xsbI4zrO1d6N6uQOHJ4cOH2bNnT56PExHq1avnGOtqD85Onz5N/fr1Wbdunc9jznztvvwSeMYYM8UYMwXIX1KgAggJCXHMUFm+fDlxcXGUK1eOvXv3Mnr0aNq2beu4cihTpgxly5YlJibG0Q0YFRXF7t27HflgTp48Sc2aNZk6dSo33nijI+t+VlaWI3Fh8+bNXT7oUVFR/PrrrzRp0oROnTplW+PQWX7yJh0/fjzXoMydjn1RgVa1alVmzJjBe++9x+nTp2nevDn79+93LADvLDQ0lJCQECZOnJhtxYGIiAiuuOIKKlasyEcffcSDDz5InTp1HOPSnLscRIS//vqLLl268NZbbxEZGekYRwnWxMPuK0HceOONNG/e3PF3gwYNHBMDFi9ezF133UVGRgbNmzdn9erVZGZmOo6fU6dOUbt2bZegzN4amN/8Z4Fknzhx8uRJevToQceOHcnKymLAgAEe0+o4S0xMRETIzMykQoUKHDhwgMWLF9O+fXtHCoa0tDREhKpVqzpWCalUqRK1a9d2zJpcsGABvXr14plnnuHnn392ORfVqFGDdu3aBejVX5yaN2+eZ9LYZ555hiuuuILXX3/dZWm8UaNGkZyc7EjSC9ZUHPlJP+ONzMxMLrvsMr8lgs7NqFGjXP6eM2cOv//+u8u2U6dOMXr0aFasWEGnTp28Tnbsyfbt2x1jTJ3t3buXVatWOf7OysqiXr16xMTEMH36dJYsWeK4GG3Xrh1DhgzJNfG2J762lP0H6G7rvnwG8G7dDT+zWCzMmjXLcYXtPK6nbNmyrF271iVz/qlTpxxNwK1bt2bTpk2ANX/XwYMHqVKlChUrVqRdu3a8+uqrWCwWRzM8WDPD28eqAbRs2ZKtW7cSHh5O2bJlc+yePHfunMtYFW9Vq1btokmmqC4ODRo04K677iIsLIzBgwfTuHFjKlSoQNmyZbOt4wfwwAMP0KlTp1xzW9mPjVtuucURbI0cOZI5c+aQkZHB1q1bHQFf69atKVGihMsyY926dcv2pV+5cuVsgeCtt97K0qVLOXjwII0aNXKkmPnjjz8YMGAArVu3Bqwzpt2DMhHh2muvZfXq1b6+ZQH3/vvvs3XrVsqVK0f58uW55ZZbAOtVfm7jijIzM3nppZdYv349r7zyiiPp9PHjx7nvvvtYt24dAJMnT2bz5s0u57cHHniAhx9+mKioKI4dO8ayZcuIjIykVKlSnDx50qVlLDw8PMdM/cp/ypYty4EDByhRogQZGRlUrlyZ06dPIyI0adIk12E5BbF//35atGiR5wVAQZ07d47vv/8+23b37slvvvmGZ599llGjRnHVVVf5nKvQ2fHjxz2OSYuOjmbFihWAdTWRcuXKUa9ePQ4cOEBISAj79+9n27ZttGzZkmbNmjFo0CCf65GflBjXGWO+MsZMB37Mx/MLrGfPnoSFhdGqVSsyMzMdCQrtPvroIx599FHH3/fcc4+jpax8+fKcO3fOMQvTvbkXrF9AW7ZsccnK7L4sj3tyQE+uueYarx7nrlOnTtr6pYJKq1atuPXWWx1/ly5dOtc8YDVr1qRFixY+7SM0NJTw8HBCQ0N57733mDt3LjfccEOOjw8PD/cqy3yLFi1YtWqVYzmm9PR0rrzySu677z4++OADRyqTNm3aOLrczp49C1iP++uuu46///7bY2qdonLgwAG6dOlCv379XP4vFouFWrVqERMTk+Nz7ReUmzdv5sknn6RTp06OLrCoqCj+/vtvRISMjAxmzpzpMpDZ3vvQqVMn7r//foYNG+a4r1mzZtoyVgQ6dOjAyJEjefDBB9m3b5+j5ebw4cNcf/31HDhwwC8tvdu3b3cZKrNt2zaaN29Oeno677zzDgkJCaxZs4YlS5YUqPXst99+c/n7u+++46GHHnIck3bOCbvT09MpUaIEZcqUYcKECVSrVq1A36EXLlzw2GJ54sQJypYty8GDB5kxYwZ33303ZcuWJTMz09GIs337dpo3b44xJl/L7eUnKKsOLBSRfwMT8/H8AitRogQPPPAAUVFRHvuz3XN23X///Tn+gzwFZZdddhmrVq3Kda3Ijz/+OM963nrrrflab9LTwuFKBRvnbkJ/aNq0qWMh+g8//JAqVar4bXbr//3f/znWNn366aepX79+tsHnnTt3pmTJkhhj2LdvH0OGDHHc17VrVz766CO/1KWgRISFCxfSpUsXrrrqKpcTf2ZmJu3bt882Q9zZxo0beeihh9i9ezc33HADtWrVYt++fVx22WWEhITwyCOPMHv2bMqUKcP27dtp2rSpV/Xq0qWLS1ogVTjq1q3L9u3bueeee4iNjaVBgwYcPHiQNWvWcO2119KtWzcGDRrkcZF4XyxcuNDl/2vPzykiPPbYY3z44Yds3LiRPXv2eJ0jMDMzM9sYyK+++gqwjif96KOPuOKKK7jmmms4cuQI8M+QoNq1azvGf+/du9dxPqpTp45LwObJokWLcq2XMYaIiIhs407BemEyb948rrzySsf3e48ePXjsscdo06YNFy5c8Glyn7v8BGX1gZrGmF5AkU6pad26NRUrVvQ5IrZYLI7cZPYxZc4aNmzIunXrcg2oittCuUoFu5tvvtnRalWiRAl69+7tt7JLly7tyBXYrFmzPM8Z8fHxLmNzLrvsMho0aEB6enq2NR0L07p16xg+fDiZmZkeZ//edtttdOzYkXPnzuVYRnJyMu3atXOZgGGxWLj55psBuPzyyzl+/Dgiwj333JOvC0tVuD799FNq1qxJVFQUNWvW5Pjx4yQlJVG1alXq16/Pc889x5dffgnAli1bfA7Qzp0752jscA52jDH07duXli1bMmbMGEJCQsjMzMzxoiAuLs5lNvTWrVv56aefXB6zd+9esrKy2LNnD6+88go33ngj9evX5/DhwyxZsoTBgwdTo0YNoqKi2LVrF2BtnbriiitcygkLC8vxdQ4cOBCwpnBJTEwErN2fzt3+TZo08Tge7N///jcvvviiY7iA83tx/fXXuwx1yo/8RBYfAcOBeKBI8wGUK1eOd999F/BtsPsVV1zhGOtSs2bNbGNiSpQoQXx8vMu4NKXUpePLL7/MduHVqFEjfv31V7Zs2cL27dtZu3at474xY8b4POsxP1auXEmJEiVyHAzevHnzXFf3+O677+jQoQOVK1d25GwE65e6fTUSZ3369NGhFMXAtddeizGG999/3+P/q1q1aqSnpyMizJw5k61bt/pU/l9//UXHjh2pVatWnjM6Q0NDHYHX4cOHXe6zz5q2i46OdlnbU0Ro3Lgx+/btcySCB2ur2Pbt29m7dy99+/bl7rvvdjwOrLMd3bMgXHHFFezYscMRdP3444+kpaU5UuDExcXx559/snTpUn777TcaN27saKUTEcc61Xb2oDYkJCSgx4Svsy/vBF4F3gJaAtnnTxey8PBwSpcu7XGwcU7at2/PbbfdBpDjuJdGjRp5NV5FKXXxsectc24VaN68OV999RVRUVFs2LDBZRaWxWJhxgx/5dL27M8//3SMhXOeherJhQsX+OSTTwBrb0BMTAzfffcdDRs2pFWrVoBra789I7xdRkaGTjYqhuz5Kj113bVt25b169dTu3Ztx2Q3b9lXaGjcuDH79+/3evC68xAAsM6+dr6YSUlJcfnsJScnc8MNN2RLQxMaGsqCBQt45JFHKFmyJCVLliQ8PJz09HSeeuopj+M9r7rqKj799FNGjBgBwE8//URsbCwJCQk8/PDDLFu2jLNnz3L8+HEOHTrEtddey+WXX+4Yj1m+fHlHTtT4+HiOHDnisjJPoPiacTQGqAKstP2917/VyZ+IiAif+nBDQkIcJ6TXXnvN42N01pBSl7aIiAiXAfM1atTg3LlzlC1blpSUFMdFW3p6OuXKlQv4LLSdO3e6zD7Nzfnz5x2tFdOmTaNSpUpkZGR4vfzONddcU6hJSJV/XbhwIVtDxfXXX8/w4cMdyxD6wt4y1KhRI/r160eNGjVo3759ns9zD8AqV66ca0CXlJTE5ZdfzsGDB7ONJ+3Tp0+2VuCNGzfy8ssvO2ZEOitTpgxdu3YlMzOThIQEqlatyrFjxyhbtixXXnkla9ascVx82V/fXXfdxZgxY7Ll11u3bh3/+c9/8pVNwVc+BWUissMY8xkwS0QygMN5PacwtG3bNt8Z7XNqDXOevamUuvQ0adLEZfUQYwxvv/02sbGxHD9+nGbNmrFv3z4sFgsNGjQgMTGRgwcPEhER4VPLvTfi4uI8LrmWk7feeosZM2Zw6NAhx0LivszAy23Wqwp+FSpUoEmTJi7bjDGUKVOGZs2aER0d7VU5S5cudekqL126NPfeey8PPPCAx+WVSpYsydmzZylZsiRnzpyhWrVqJCcnc+bMGSZNmsTTTz/Nrl27GD16NM8//zzh4eEuqScSExOpXLmyxxmjnvKBfvjhh9SpUyfHtSe7d+/O+fPnGTZsGN26dSMmJoby5cvTqlUrwsPDycjIIDExkbZt2wJQqlQp7rjjjmyBY0xMDPfcc49X71lB5WdM2UKgiTEm0hjT098Vyoun2VgRERG69qNSyq+ioqK48847XbbddtttNGzYkBMnTnDXXXfx22+/OfKf3XzzzQwaNIg5c+b4vS4rV67MlqQ3L82aNWPChAl07NiRjIwMHY5xCbn33ns9Bio9e/bkpptucgQkzsHZsWPHyMrK4vTp045tW7ZsydaV+NRTT1GhQgWPYxdr165N6dKlHQnWb7vtNg4dOsSCBQt46623aNiwIZ07d6Z+/fqMHTuWzp07IyJs2rSJc+fOkZSURJUqVWjUqJFXrXneJFkvW7Ys77//PjfeeCNnzpzh+PHj1KhRg9tuu43IyEiuvfZarr/+esfjr7zySkcqK4vFwsaNGzl79myhja3MT1B2F/Aw8B/8tMySLy7FBaCVUoWvRIkS2WZmAzRu3JimTZsSGhpKxYoV2bx5M3Xr1qVGjRpMmDChQBnUP/zwQ4/bExISck3E60lUVBRr166levXqlCpViqioqHzXSxUv1apV8zikxz5IPTIykiNHjtCnTx9H8DNw4EBiYmJ46aWXyMjIwGKxEBYWRlpamtc9UQ0aNKBBgwZcd911fPPNN46gLDU11WUiSefOndm1axd16tShfPny/PDDD3z99deOlrLbbruN++67zz9vho09qMrMzCQ8PJwmTZpw//33c9ddd+U4qe/5558nOTmZvn375nu/FStW9Onx+QnK7hWRD0WkP/BePp5fIBqUKaWKUs2aNfnPf/4DWFcVWLFiRb6HT7iLjo72eVmWnJQrV46ePa2dGY8++qgj3YhSjRs3ZtWqVTz44INMmDCBxMREwsPD2b9/PxERESxZsoTp06dz7733cuzYsVzX43R2+eWXc/vttzsCwlatWjF//nzHMmp24eHhTJo0CbDmJ7z99tvJysoiMTGRSpUqUbJkyaDIeRcWFsYtt9ySbSKML3zN4pCfoKy7MWa2LaP/1Hw8v0A0KFNKFTX7VXepUqWYPn26y3255UfKS7t27RyJLf/66y9WrFjhWEsvP+wD+ytWrJjnmorq0hEZGcn8+fO5++67ueeee/jxxx954YUXOHDgAM2aNWP16tU0atSIyMhIKleuTP369X3ex8cff+xoTXZP1Az/HENt27alQ4cOXHnllY7VJgIlLS0t4BNy3HXs2NGnx/t0eWeMKY81kJsrIl8YY57xaW9+4CmXjlJKFRX38az2tTLtyVhzkpmZybJly6hcuTIlS5bkiiuuoFSpUtSqVYvp06c71i+0WCxcc801gXwJ6hITHh7OgQMHaNSoEWFhYTRq1AiAqVOn0q5dO55//nnHY7t3706tWrV83oc99Yo9NUte2rZty1tvveXzfnxRtWpVx8oehcXXhiRfW8rGA92BssaYQUDhTEdQSqliwn7F7y4uLs4lUea6deuYN28ea9as4Y8//nBsv/fee2nYsCGRkZE0bdqU+fPnZ8tWrlRBXX/99dm63Q8cOJAtF1f9+vULpZW1dOnSfl+6zd2TTz6Za3LlYODrQIj3sOYpuw44Doz2e42UUqoYs6+75z7j8Y8//qBBgwa0b9+e5557jiuvvJLGjRsjIpQpU4bU1FTHVbVzK9tdd91V6K9BXfz69euXbVv58uWLdCzXO++8U2T7DhY+tZSJyEER2QB8C5QHlgakVkopVYw9/vjjfPbZZy7bkpOT2bt3LydPnqRly5bs2rWLKlWqkJqaSmRkJDt37gz6q3h18fA0K7Bnz54FGtReUMEwuL+o+Tqm7EOgEVARmA7oHGullHJTpUoVKleuTHp6uuNLLiwsjMzMTP766y+eeOIJqlSpwtGjR9m3bx8RERFs2rSJ2rVrF3HN1aXMnkRVFR1fx5RdDrwnIveJyA+2rP5KKaXctGrVii1btmTbHhsbS7Vq1QgJCSEyMpLbbruNunXrsm3bNk2CrdQlzteg7F8icjAgNVFKqYuIpwH/Z8+e9TiTslKlShw8eFC7L5W6xPm69qXfW8aMMbcCPYAU4HsR+SPXJyilVDFQokQJEhISSE9PR0QoWbIkr7zyiscxO8YYkpKStKVMqUtcfpLHBsJGYDOwy9Odxpgexpj1xpj1J06cKNSKKaVUfr3wwgtMmjSJgwcP0qBBg1wHUYuIz0uyKKUuLv5ZG8RLxpgWwMdum18G/gZKANOwrqvpQkQmAZMA2rZtK+73K6VUMKpSpQrh4eFs27aNDh065PrYqKgoXbFEqUtcoQZlIrIN6Oq8zRjTRkSOABm2FQOUUuqicvLkSapVq5brY+6+++5Cqo1SKlgValCWg+bGmDsAAcYVdWWUUsqfatasyebNm/N83D336AIpSl3qijwoE5EZRV0HpZQKlBYtWrBixYqiroZSqhgIloH+Sil1UWrYsCFPPPFEUVdDKVUMaFCmlFIBFBoaSuvWrYu6GkqpYkCDMqWUUkqpIKBBmVJKKaVUENCgTCmllFIqCGhQppRSSikVBDQoU0oppZQKAhqUKaWUUkoFAQ3KlFJKKaWCgAZlSimllFJBQIMypZRSSqkgoEGZUkoppVQQ0KBMKaWUUioIaFCmlFJKKRUENChTSimllAoCGpQppZRSSgUBDcqUUkoppYKABmVKKaWUUkFAgzKllFJKqSBQ6EGZMaasMWa0MeZ/hb1vpZRSSqlgFVYE+6wAzAfa2DcYY/oA6UBDEXm3COqklFJKKVWkCj0oE5E4Y0xT+9/GmBLArSLyiDHmdWPMjSKy0vk5xpgeQA/bn+nGmG1+rlY14GQQl1dcyiwOdQxEmcWhjoEoszjUMRBlFoc6BqLM4lDHQJRZHOoYiDKLQx0DUWYg6tg074dYBTQoM8a0AD5229zD7e+qwHnb7yeASMAlKBORScAkW5nrRaStn+vp1zKLQx0DUWZxqGMgyiwOdQxEmcWhjoEoszjUMRBlFoc6BqLM4lDHQJRZHOoYiDIDVUdvHxvQoExEtgFd3bcbY65w+vMUUNb2ewTwdyDrpJRSSikVjIpioH8F4B7gamNMExG5ACwxxjwHVHLvulRKKaWUuhQUxZiys8DrbtvG+lDEJP/WKCBlFoc6BqLM4lDHQJRZHOoYiDKLQx0DUWZxqGMgyiwOdQxEmcWhjoEoszjUMRBlFmkdjYgEYP9KKaWUUsoXmjxWKaWUUioIaFCmlFJKKRUENCi7hBljFhtj2hV1PVRgGGPCjDHvGmP8NkbCvUxjzNXGmPXGmLrBWsdAlWmMaW+M+TOY6xhsZRaHOhaGgh43xVlBjptLQVFk9M+TMaYT8APQWEQSjTFhwA5gsIhMLmDZZWxlNReR83k9Ph/l3w9EAeHAHhH5oYDltQKuAcoA1UTk/YLXEowxd/BPfjh/lNcAGA3EA4tEZGYBywsFXgESsM7KHeOHOj4NtAeygFYico0fymwNvASsAqqIyKACltcceArYDSSLyHcFKK4ssADoZSu7G9a0M/WAfiKSWtAygdNAQZI5u9dxFLAJuBb4rz/qaIzpCaQCtwNvi8jRgpZpcxNg8lGWpzoOAGrY7usrIsl+KPMW4DKgOjBLRPb5ocxfsOaTrAr8KSLjCljeeGAL1nPmMBGJ8UMd+wOHgfrAEF/fS/dzONbGiwIdNx7K3EjBjhtPZd5MAY4dD+VVoYDHTQ7fhwU5bjzV8yoKcOx4KC+eAh43Hsp8Ei+Pm6AMykRkoTFmPvAa8A7wMLATWGyMeR9IxvqhPgl8AXwFtBGR/3hR/IPADOAxY0xNoBmwDuvyT1OAecA4oLOIPJCP6m8QkTnGmIq2uhUoKBORLcaYc1jfi58LUpadMcYAbQGvE9p5aSdwAIj2Q1n3YT2ppmI90fjDn8B0rCfxnn4q8yBQCmtdOwAFCsqAO4DFIjLfGLMUyHdQJiJnjDGnnDY9LiJdjDGPAA8APgfO7mWKyH7rx8lvdRwkIseMMVcCjcnHF5eHOk40xvwL61Jucf6opzHmPuBX4DZ/lIf1Amkp1s9Sip/K7AvMBdKwBin+KPNFEYkxxrwKfO2H8rYCNbEeO78CPgdlHsq8QUQ+sF2EPQxM9bFI93N4eEGPG/cyReSHghw3OdTzlQIeO+51fLigx417mcaYNApw3HgqE2ssUJBjx728EAp43Hgo0+vjJpi7L/8AWhljamMNmE4AFbG2ciQBj9mS0x4RkdHAC16WGwkMwxq5rgD+FpGRQCtbuZuxHnTd81NpETlm+/UBYGh+yvBQ5gHgDbx/jXl5ED8FeE6OAf2AicBAP5QXBRwXkfGAX9ZDFZFjImLB+r8vUEuek/uwHsAfAP39UN6XQFNjzL+Bcn4oz1kp20/7yhlBx/alUg7ItB3f/ip3BtbPqD9aR0OAliKypcAV+8dYWx0zsAYS/nAz1i+AOOBxfxRo+2IpAZQWkTN+KPI+EekH/Bv/XXyNtgVkTfmnBcVrHs7hBT5uAvS94FJmQY8dT3Us6HHjVuZw/HDceKhngY4dD+UV+Ljx8L/x+rgJ5qAMYDDW4MHe2tQBa9A0HShp23YeQETS8yrMGBMF1MK6ykBJ4Eqnux25QUQk2ZvyctnPPVhbjI7l9VgvyrrTXiegfEHLs2mAtRuvLdDFGBPhhzKbABax5ljxRwtsPHDW9rvfPqe2VsIaIhLrpyKrAUkikoVb/r18EmCKiEwH8tPNlps0288I4Iify/YLY00u3RP4yBhT3U9l3mn7NQ6o7YcimwDhxpgXgDrGmM5+KLOx7ecprN0m/nDYlpw7CWtXlL88SgF7AJyE2n7Wwtpt7w9bRGQqsA9r673P3M7hfjlu/Pm94KlMfxw7buX55bhxKjMcPx03bu9lgY8dt/L8ctx4+H97ddwEZfelMeZW4Bas3Yz/BkpjverZC3TDGlQ0McbUt/1sJyIr8igzDHgf+FhEdti6Lj8GvjfGvIK1y62krbzOIjI/n3XvCryJdZxEeeCJ/JTjJMIY8w5gwfdmeI9EZJhtDNjdWFse/XHFWxt40hgTC/zkh/JmAQONMc9jbYnyl3uAfP1vc/Ad8JYxpgn+6bZtCLxojNmF9coy32wBaDesLW9tgJnGmBexjY3xU5kGa5DyGDDED+WNwTrmczTWC7LZfijz37bj/SrgVV/Lcy8TKCsi/Y0xD2INopP8UMcexpg1WFvsPypoHW1ljrJ1l9Qkn60z7mWKyEagqYh85ac6/m6M6Y21JWaYn8p8xhgTDdTB2vLsa3ldcT2H++O4cSnTGDOcAhw3OdSzIQU4djyUhx+OG5cyReSJghw3OdTzXEGOHQ/l+eO4cS/zCbw8bi7p5LG24K+B7apKKaWUUqrIBHv3ZaC1s92UUkoppYrUJd1SppRSSikVLIpdS5kxpqMxJl+DNz2UVeySDiqllFLq4hSUA/2dGbfkqcA0/Dd7xVNCSKWUUkqpQhf0LWW2nCaLsOatWi4i+/1Y9hms02iVUkoppYpU0AdlEJDkqUoppZRSQSXog7IAJU9VSimllAoqQR+UYUueaox5C5hqyy1W21gXVy4QD0kHlVJKKaWKhKbEUEoppZQKAsWhpUwppZRS6qKnQZlSSimlVBDQoEwppZRSKghoUKaUUkopFQQ0KFNKKaWUCgIalCmllFJKBQENypRSSiml3BhjvjbG9DXG7Lb93GeMqRHQfWqeMqWUUkopV8aYBiJyyBizUEQ6GWO+BKYBLYAOwBYgHDgNXC4iPY0x9wF1bbcvbMtEek1bypRSSiml3IjIIbdNS20/5wEHRORDoIWIDANK2u57GUgFkrAGbz4Jy19VlVJKKaUuWedtP894uG8a1rW6I30tVIMypZRSSikPjDEdsK633QS4CagD1ATa2NbMbuL083pgFPAuYAE+93l/OqZMKaWUUqro6ZgypZRSSqkgoEGZUkoppVQQ0KBMKaWUUioIaFCmlFJKKRUENChTSimllAoCGpQppZRSSgUBDcqUUkoppYKABmVKKaWUUkFAgzKllFJKqSCgQZlSSimlVBDQoEwppZRSKghoUKaUUkopFQQ0KFNKKaWUCgIalCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDMqWUUkqpIKBBmVJKKaVUENCgTCmllFIqCGhQppRSSikVBDQoU0oppZQKAhqUKaWUUkoFAQ3KlFJKKaWCgAZlSimllFJBQIMypZRSSqkgEBboHRhjWgMvAauAKsAhIAKoB/QDjO3nESBeRH4IdJ2UUkoppYKNEZHA7sCYSsA44ADQATgpIl2MMY8A4Vhb69JF5AdjzGwR6eqhjB5AD4CyZcteHRUVFdA6K6WUUkr5w4YNG06KSIQ3jw14SxlwHzAX+B5YBrxq234CuB5rS9kq27bSngoQkUnAJIC2bdvK+vXrA1lfpZRSSim/MMYc9vaxhTGmrBqQJCJZwOtAmm17BNYuyxjb7wCphVAfpZRSSqmgUxgtZd8BbxljmgDRwBpjzIu4jSkzxtQAvi6E+iillFJKBZ2AB2UiEgu8nMfD3gh0PZRSSimlgpmmxFBKKaWUCgIalCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDshwkJiZy//33s2TJkoDva/LkyTz99NMB349SSimlgtdFF5S98cYbdO7cmaSkJLZu3cp1110HwObNm+nevTsnTpzgww8/zLOcKlWq0KZNm0BXF4BOnToVyn6UUkopFbwKI09ZoXr//fe56667qFy5MtOnT6dKlSrExMQQEhJCz5492b9/P3PmzOH999/nhRdeICEhgeuuu46VK1fy888/ExMTwwcffMBNN93Epk2buPXWW13K79evH3Xr1mXz5s288cYb9OvXj/T0dG6++WZWrVrF2LFjOXz4MLNmzaJBgwZs27aNQYMGMWvWLE6cOAGAMYbevXvzf//3f9SrV4+srKwieKeUUkopFUwuupaycuXKUb16dQ4cOEBmZibdu3dn1qxZrFixgltuuYXrr7+ecuXKAdC9e3caNmzIm2++SdmyZYmLi2Ps2LF0796d559/niZNmmQr/8CBA5w5c4ZevXpRq1YtbrnlFm688UZeeOEFWrduzffff89HH31EmTJlEBFSU1OJi4ujX79+lC1blrJlyxIdHc2OHTuIi4vjv//9L507dy7st0kppZRSQeaiaykDeOihh+jfvz+PP/441113HQ8++CAPPvggoaGh2R5bvnx5AEqUKEFGRkaeZQ8dOpRDhw7x2muv8c4777jcZ1/c3RhDp06daN26NU2aNKFq1aoA/Pvf/yYkJIR69eoV9CUqpZRS6iJzUQZl9913H2+88QZffPEFYWFhlC5dmiuuuAKA1atXExcXx+rVq1myZAkbN25k7969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eTInTpww+by4f/8+Q4cOZePGjfTq1euBxzdu3LjAG2128oqjoyPnz5/X3zRz8thjj2nSJeDKlSv5GuPFldTUVL0HNqeBHRgYyLFjx4x+JiMjg5kzZzJlyhQ6duyIg4MD5cqV45tvvmHEiBF06dKFgIAAGjZsyBdffMG4ceM0e0Av6CGgfv36nDlzxuIxQJdYExUVRalSpXjsscdYvXo1TZo00e9v164dW7Zseeg7bZiLMspszM2bN/ONDQkODubAAW17vm/atIlu3brRtGlT+vbty/Tp07l//z6bNm2ia9eugC7oesuWLUydOpXmzZszcuRIxo4dy7179/LUtPnxxx/1nzOGq6urPgC+dOnSDBgwgKFDh+Lh4YGbmxvvvPMOLVq0ICQkpFD/j2bNmtGpUyeOHTvGzp07jR6TlpbGihUrjLr/c9+swsPD6dq1KxcuXAAKt9ySk379+tGsWTN9PODhw4cZOXIk27Zto1+/fkVaYsASpJT6uEYpZaEuoFJKZs2axTPPPMOhQ4c4efIkf/75JwEBAfTt27fAz2bHXRmLK7p165bZRlmpUqUYOHAgJUuWNLo/KSmJ1atXm5URmk22J8/d3T3fxJBs/P39efvtt0lNTWXp0qUmyf/777955ZVXTC4b0bJlS37//fd896empupj3hwdHalWrVqeY/z8/IiMjLTYi5KcnIy7u7tFMuyNI0eO6GvC5cxqbNGiBbt37yYhISHPZ+bOncvgwYOZOHGiPnO2c+fOpKSkGJybzs7OfPzxx5QuXZru3btr4i2LiYkxGioDuvp/WpWr2LNnDy+99BIAlStX5t69e7i6uur3CyEYPnw406dPL5axg9ZGGWU25v79+/lmWtapU4dz587pvQ1acP36df2yR7ly5RgwYADz588nOjpav7wghOC9995j4MCBBksOL730EocPH9ZP3piYGIQQeHp6WqRTlSpV9B6SbO7fv2/Ue7Zu3ToaNmyof//hhx9y8eJFAH7//XcWLlzIpEmTOHfuHDNmzKBVq1Zs3bqVmTNnkl14OCYmho8//tjgInTy5EmefPJJ/c00MTHRrKBygFatWrF//35A99RYvnx5wsLCaNy4sdUKNVqDatWqcenSJbZs2cKCBQtM/tzevXtp3749vr6+vPnmmxw/fpyffvqJVq1a6YP886NChQpERERQt27dPMtmd+7c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\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plot(2492, p['index'], p.model, p.GridREx)" - ], + "execution_count": 21, + "outputs": [], + "source": [], "metadata": { "collapsed": false, "pycharm": { From a8b42b9d4634b4a823c6e6ea67bdb09638d8ba71 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 23:36:01 +0200 Subject: [PATCH 28/67] wip --- demo/DemoRegressionScaled.ipynb | 209 +++++++++++++++++--------------- 1 file changed, 112 insertions(+), 97 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 3c90fa85..cf02cb48 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 38, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 39, "outputs": [], "source": [ "def plot(testB, x, pred, extracted):\n", @@ -58,7 +58,65 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 75, + "outputs": [], + "source": [ + "def splitTest(data):\n", + " b = bartels[(bartels.n == 2490)]\n", + " data = data[(data.index >= b.t1.values[0])]\n", + "\n", + " b = bartels[(bartels.n == 2495) | (bartels.n == 2501) | (bartels.n == 2506)]\n", + " idx = np.zeros_like(data.index, dtype='bool')\n", + " for _, row in b.iterrows():\n", + " t0, t1 = row.t0, row.t1\n", + " idx = idx | (data.index >= t0) & (data.index < t1)\n", + " return data[~idx], data[idx]\n", + "\n", + "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", + "data = pd.read_csv(f'data/halffuzzycoefs4B3.csv', parse_dates=[0], index_col=0)\n", + "train, test = splitTest(data)\n", + "train.to_csv('data/alltrain.csv')\n", + "test.to_csv('data/alltest.csv')\n", + "\n", + "data = pd.read_csv(f'data/pruned.csv', parse_dates=[0], index_col=0)\n", + "train, test = splitTest(data)\n", + "train.to_csv('data/prunedtrain.csv')\n", + "test.to_csv('data/prunedtest.csv')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 73, + "outputs": [ + { + "data": { + "text/plain": " Bmin Bmedian Bmax Btrend Bhalf1 Bhalf2 \\\n2016-03-04 00:00:00 4.0 7.5 12.3 -0.080751 0.009474 -0.106862 \n2016-03-04 01:00:00 4.0 7.5 12.3 -0.086067 -0.012065 -0.101174 \n2016-03-04 02:00:00 4.0 7.5 12.3 -0.090328 -0.029534 -0.094717 \n2016-03-04 03:00:00 4.0 7.5 12.3 -0.094527 -0.046781 -0.089474 \n2016-03-04 04:00:00 4.0 7.5 12.3 -0.098312 -0.062166 -0.087105 \n... ... ... ... ... ... ... \n2017-07-02 19:00:00 1.6 8.0 18.2 0.168533 0.139028 0.004049 \n2017-07-02 20:00:00 1.6 8.3 18.2 0.165550 0.157085 -0.019089 \n2017-07-02 21:00:00 1.6 8.3 18.2 0.161067 0.174393 -0.041862 \n2017-07-02 22:00:00 1.6 8.5 18.2 0.156652 0.188077 -0.061781 \n2017-07-02 23:00:00 1.6 8.5 18.2 0.151061 0.198158 -0.086761 \n\n Bthird1 Bthird2 Bthird3 GCRmin ... GCRhalf1 \\\n2016-03-04 00:00:00 -0.001462 -0.016066 -0.068720 -3.883201 ... -0.034045 \n2016-03-04 01:00:00 -0.004731 -0.012913 -0.074356 -3.883201 ... -0.035872 \n2016-03-04 02:00:00 -0.007107 -0.008399 -0.080310 -3.883201 ... -0.038517 \n2016-03-04 03:00:00 -0.010202 -0.001853 -0.085397 -3.883201 ... -0.040322 \n2016-03-04 04:00:00 -0.012979 0.006375 -0.089585 -3.883201 ... -0.042501 \n... ... ... ... ... ... ... \n2017-07-02 19:00:00 -0.032341 0.005700 0.162350 -4.670171 ... -0.059403 \n2017-07-02 20:00:00 -0.032566 0.005048 0.159932 -4.670171 ... -0.058878 \n2017-07-02 21:00:00 -0.033405 0.002897 0.157075 -4.670171 ... -0.058062 \n2017-07-02 22:00:00 -0.033445 0.001775 0.153994 -4.670171 ... -0.057460 \n2017-07-02 23:00:00 -0.033619 0.001199 0.149502 -4.670171 ... -0.056252 \n\n GCRhalf2 GCRthird1 GCRthird2 GCRthird3 GCRfourth1 \\\n2016-03-04 00:00:00 0.060357 0.002682 -0.072897 0.065471 0.007162 \n2016-03-04 01:00:00 0.061504 0.000701 -0.070397 0.064501 0.006544 \n2016-03-04 02:00:00 0.062143 -0.002516 -0.066904 0.064396 0.004833 \n2016-03-04 03:00:00 0.063626 -0.002422 -0.062109 0.066305 0.004309 \n2016-03-04 04:00:00 0.064246 -0.005347 -0.056971 0.065904 0.002981 \n... ... ... ... ... ... \n2017-07-02 19:00:00 0.016938 -0.075540 0.018295 -0.025042 -0.074034 \n2017-07-02 20:00:00 0.015490 -0.076592 0.020250 -0.027076 -0.076765 \n2017-07-02 21:00:00 0.014373 -0.076522 0.022473 -0.028479 -0.079294 \n2017-07-02 22:00:00 0.013574 -0.077431 0.025158 -0.029432 -0.080745 \n2017-07-02 23:00:00 0.012860 -0.077216 0.027493 -0.031331 -0.082300 \n\n GCRfourth2 GCRfourth3 GCRfourth4 V \n2016-03-04 00:00:00 -0.092158 0.046471 0.069499 410.0 \n2016-03-04 01:00:00 -0.095190 0.049612 0.069178 400.0 \n2016-03-04 02:00:00 -0.098934 0.049670 0.071374 395.0 \n2016-03-04 03:00:00 -0.102389 0.052382 0.072217 408.0 \n2016-03-04 04:00:00 -0.105296 0.052220 0.070273 406.0 \n... ... ... ... ... \n2017-07-02 19:00:00 -0.027912 0.071203 -0.054068 477.0 \n2017-07-02 20:00:00 -0.026023 0.071905 -0.053493 442.0 \n2017-07-02 21:00:00 -0.022508 0.073575 -0.054818 437.0 \n2017-07-02 22:00:00 -0.020645 0.074790 -0.054839 436.0 \n2017-07-02 23:00:00 -0.017545 0.074579 -0.054656 430.0 \n\n[9720 rows x 23 columns]", + "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 \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
BminBmedianBmaxBtrendBhalf1Bhalf2Bthird1Bthird2Bthird3GCRmin...GCRhalf1GCRhalf2GCRthird1GCRthird2GCRthird3GCRfourth1GCRfourth2GCRfourth3GCRfourth4V
2016-03-04 00:00:004.07.512.3-0.0807510.009474-0.106862-0.001462-0.016066-0.068720-3.883201...-0.0340450.0603570.002682-0.0728970.0654710.007162-0.0921580.0464710.069499410.0
2016-03-04 01:00:004.07.512.3-0.086067-0.012065-0.101174-0.004731-0.012913-0.074356-3.883201...-0.0358720.0615040.000701-0.0703970.0645010.006544-0.0951900.0496120.069178400.0
2016-03-04 02:00:004.07.512.3-0.090328-0.029534-0.094717-0.007107-0.008399-0.080310-3.883201...-0.0385170.062143-0.002516-0.0669040.0643960.004833-0.0989340.0496700.071374395.0
2016-03-04 03:00:004.07.512.3-0.094527-0.046781-0.089474-0.010202-0.001853-0.085397-3.883201...-0.0403220.063626-0.002422-0.0621090.0663050.004309-0.1023890.0523820.072217408.0
2016-03-04 04:00:004.07.512.3-0.098312-0.062166-0.087105-0.0129790.006375-0.089585-3.883201...-0.0425010.064246-0.005347-0.0569710.0659040.002981-0.1052960.0522200.070273406.0
..................................................................
2017-07-02 19:00:001.68.018.20.1685330.1390280.004049-0.0323410.0057000.162350-4.670171...-0.0594030.016938-0.0755400.018295-0.025042-0.074034-0.0279120.071203-0.054068477.0
2017-07-02 20:00:001.68.318.20.1655500.157085-0.019089-0.0325660.0050480.159932-4.670171...-0.0588780.015490-0.0765920.020250-0.027076-0.076765-0.0260230.071905-0.053493442.0
2017-07-02 21:00:001.68.318.20.1610670.174393-0.041862-0.0334050.0028970.157075-4.670171...-0.0580620.014373-0.0765220.022473-0.028479-0.079294-0.0225080.073575-0.054818437.0
2017-07-02 22:00:001.68.518.20.1566520.188077-0.061781-0.0334450.0017750.153994-4.670171...-0.0574600.013574-0.0774310.025158-0.029432-0.080745-0.0206450.074790-0.054839436.0
2017-07-02 23:00:001.68.518.20.1510610.198158-0.086761-0.0336190.0011990.149502-4.670171...-0.0562520.012860-0.0772160.027493-0.031331-0.082300-0.0175450.074579-0.054656430.0
\n

9720 rows × 23 columns

\n
" + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.read_csv('data/alltrain.csv', parse_dates=[0], index_col=[0])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, "outputs": [], "source": [ "def getTrainTest(data, testB):\n", @@ -82,7 +140,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 4, "outputs": [], "source": [ "def gridex(model, train, test, normalization):\n", @@ -100,20 +158,26 @@ " return gridREx.brute_predict(test, 'default'), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", "def cart(model, train, test, normalization):\n", - " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", + " CART = Extractor.cart(model, max_depth=10, max_leaves=10, normalization=normalization)\n", " CART.extract(train)\n", " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", "def cosmik(model, train, test, normalization):\n", - " COSMiK = Extractor.cosmik(model, max_components=10, k=150, patience=10, close_to_center=True,\n", + " COSMiK = Extractor.cosmik(model, max_components=10, k=125, patience=12, close_to_center=True,\n", " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test, 'default'), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization):\n", + "def cream(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", + " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])\n", + "\n", + "def exact(model, train, test, normalization):\n", + " CReEPy = Extractor.creepy(model, clustering=Clustering.exact, depth=5, error_threshold=.5, gauss_components=10,\n", + " output=Target.REGRESSION, normalization=normalization)\n", + " CReEPy.extract(train)\n", " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" ], "metadata": { @@ -125,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 7, "outputs": [ { "name": "stdout", @@ -136,39 +200,29 @@ "GridREx\n", "CART\n", "COSMiK\n", - "CReEPy\n" - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/3576493649.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/2214823521.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 34\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n\u001B[0;32m 35\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 380\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 381\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 382\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 383\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + "ExACT\n", + "CREAM\n", + "2492\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "ExACT\n", + "CREAM\n", + "2493\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "ExACT\n", + "CREAM\n" ] } ], "source": [ "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", "\n", - "extractors = ['GridEx', 'GridREx', 'CART', 'COSMiK', 'CReEPy']\n", + "extractors = ['GridEx', 'GridREx', 'CART', 'COSMiK', 'ExACT', 'CREAM']\n", "\n", "TESTB = [i for i in range(2491, 2509)]\n", "\n", @@ -178,12 +232,12 @@ "\n", "missed = {name: [] for name in ['BR'] + extractors}\n", "\n", - "for testB in TESTB:\n", + "for testB in TESTB[:3]:\n", " rules['BR'].append(testB)\n", " missed['BR'].append(testB)\n", " print(testB)\n", "\n", - " data = pd.read_csv(f'data/halffuzzycoefs2B.csv', parse_dates=[0], index_col=0)\n", + " data = pd.read_csv(f'data/pruned.csv', parse_dates=[0], index_col=0)\n", " train, test = getTrainTest(data, testB)\n", "\n", " predicted['index'] += list(test.index.values)\n", @@ -197,16 +251,14 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(extractors, [gridex, cart, cart, cosmik, creepy]):\n", + " for name, fun in zip(extractors, [cart, cart, cart, cosmik, exact, cream]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)\n", - "\n", - " break" + " missed[name].append(miss)" ], "metadata": { "collapsed": false, @@ -217,55 +269,26 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 15, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "75.32443332103857 45.80166313557053\n", - "65.44680302846326 41.183118869594864\n", - "65.44680302846326 41.183118869594864\n", - "82.47813971748161 33.34135118914507\n", - "80.09439063082522 39.23197875360054\n" + "90.20644923758856 47.26039351140736\n", + "90.20644923758856 47.26039351140736\n", + "90.20644923758856 47.26039351140736\n", + "73.38650661174208 31.857409982273143\n", + "71.27591988138312 36.1748132343631\n", + "70.26005494494277 33.783890819165826\n" ] }, { "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADP6UlEQVR4nOydd3gU5dbAf29C7713aSKINAERkCsWrlf97Fx7R4OiXntHQYqooPSOoCiKBaVI7713QugtAUINCSFlz/fH7C67yW6yLQ3O73nmSaadOTsz78yZ8573HCMiKIqiKIqiKLmPsJxWQFEURVEURfGMGmqKoiiKoii5FDXUFEVRFEVRcilqqCmKoiiKouRS1FBTFEVRFEXJpaihpiiKoiiKkkvJdkPNGFPJGDPGGLPWZVkhY8wQY8z7xphxxpj62a2XoiiKoihKbiMnPGo3A9MA47LsdeCQiPQFBgJjc0AvRVEURVGUXEW2G2oiMhWIS7P4LmClff1WoKkxpkR266YoiqIoipKbyC0xahVwN97O25cpiqIoiqJcteTLaQXsnACKu8yXsC9LhzHmReBFgKJFi7Zo2LBh1munKIqiKIoSJOvXr48VkfL+7JNbDLUZQFtgqTGmCbBZRM572lBERgGjAFq2bCnr1q3LPi0VRVEURVECxBhz0N99cmLUZ0fgCaCyMeYjY0xh4FugpjHmI+BN4Lns1ktRFEVRFCW3ke0eNRFZDCz2sKp7duuiKIqiKIqSm8ktgwkURVEURVGUNKihpiiKoiiKkktRQ01RFEVRFCWXooZaLmfevHnccsstOa2GoiiKoig5wBVtqC1btozKlSszZ84c57K9e/fSrl07Pv30UwA+++wzhg8fzvDhw3nsscfc9r/xxhv5+++/08k9fvw499xzD926dWPkyJE8/vjjHD9+nHfffZdbbrmFMWPGMGbMGJ566im3/RzrR48eTY8ePZgyZUqmv6Fz587O/ydMmMCePXs8bnfgwAGefvpp5/xHH32UqWxPHDlyhHvuuYe77rrLuUxEaN26Nd26dePChQs+yxowYADnzp0DUGNTURRFUQIgt+RRyxJuvvlmSpYsye233+5cds0111CvXj26dOkCwB9//MHChQspXbo07du3d263cOFCHn/8cb755hvuvvtuN7kVK1akefPmNGzYkK5du7Jv3z5Wr15Nly5dOHfuHM8//zz9+vXj+++/d9vPsf6FF15gy5Yt9O3bl0ceeYSJEyeSkJDAqVOn6NSpEzfddBN9+vRBRKhSpQoASUlJLFmyBICaNWvy1ltv0bx5cyIjI+nTpw9LliwhKiqKcePG0bp1a3799Vd69+7N9u3bmThxIvXq1ePIkSO89957dO3alfLly1OjRg327t3LhAkTnDpWq1aN5s2bc/ToUWbNmkWXLl2YMmUKDRs2pFOnThQrVowhQ4ZgjOH48ePceuutFCxYkK5du/Lxxx+zYMECHn74Ydq3b8/SpUtp1aoVYWFhREdHM2bMGB599FEGDBhAtWrViIyM5KmnnuLw4cO8/vrrvP7664wbN45vvvmGf/75hwoVKlC4cGFeeOGFkN4XiqIoipJXuKINNV8YMWIEb7/9NmfOnKFTp040btwYgH/++Yf+/fszffp01q9fT7NmzejRowfh4eF8++23ACxevJgLFy5QoUIF7rrrLpYuXcrWrVsZNGgQUVFRHo8XGRnJsGHDmDFjBqNHjwagXr16rFixgoIFCzJx4kSaNGnC9OnTWbFiBRcuXOD777+nQIECdOjQwSmnWrVq2Gw2lixZQkxMDB06dGDBggU8++yzAFSuXBmAXr168dlnn9GgQQMeeeQRDhw4wP/93/8RFxfHq6++SseOHT3q+cUXX3DvvffStm1bYmNjqV27NgDx8fH8/PPPLFu2jISEBG699VZWrlxJrVq1nAbagAEDuPfee2nevDkAHTp0oHLlyjz//PPs2rWLXbt28emnn7Jz504+++wzfv75Z4oVK0a3bt14+umnmTt3Lnv37uW2226jSZMmwV5iRVEURcmzXFWGWlRUFLVq1Uq3fMyYMYgId9xxBzfffDMFCxYkNjaWCRMmcN111/HVV1/x008/MWTIELf9OnbsSNeuXd2WNWnShNdff92rDg0aNCAiIoKLFy/y999/061bN15++WXWrFnD4cOH6dWrV6a/Y/r06Zw9e5a3336bBQsWkJiYiDEGAJvN5vzfgYgAuC0vXrx4umWuVKhQgQcffJCHH36Y33//na+++sopy5M8h8xTp06RnJycTp4xBhHBZrN53L9YsWIYYyhUqBCtW7emVatWTJgwgR9//JFRo0Zlek4URVEU5UrkijbUli1bxtmzZ51GxqpVq3j//feJiopi1qxZtGnThjFjxrB5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As7tmzRpQt++ff0+jmss2qxZs/jf//6HMYZy5cppnFoOsG/fvgyNqcy6XGw2GzNmzACsJMoOL63DUFu9ejVr164NSLeTJ09SvXp1Lly4AMDSpUvZtGmTx21nz57N6tWrAzqOooSSY8eOeVyekJCQYajIu+++y44dOyhdujQbN250Lj906BARERF8++23rFy5MuT6ZsSqVauc///zzz/s3r07W4+vKMFyRRlqN9xwAydPniQlJYWCBQuyd+9ewsIu/8Q6derQpEkTt33KlCnjk0fN0zbr1q2jdevWzvny5ctTpkwZSpUqFXR3meI7ixYtYsOGDR6Xr1+/niFDhvDXX3+lW3/mzBmGDx/Oxo0b+eOPPwDLUKtZsybGGJKTk6lUqRIzZ85k0aJFbp4Eh+GVGbt27eK2227jyJEjgBWv5i3P3sWLF93SzBw6dMinY4SSQA1S5crio48+8vjMK1eunNf7NzY2lqioKPbt28djjz3G1q1bnesOHjxI48aNefvtt9NV+7h06VI6WaHMc/nuu+86B5qdOXOGPXv2hEy2omQHV5ShVq1aNT744AMAKleuzK5duyhRooRz/Z133smNN96Ybr98+fJl+GCYMGECPXr0QESIiYmhXLlyAKxevZp27do5t6tRowa1a9emWbNmbl+TvrBs2TIdjOAjf//9t9v8xYsX2b9/f7rzt2vXLtatW0fVqlXdKgwsXryYJUuW8MMPP9CwYUOGDBlCy5YtSU5Opnr16tSuXZtChQpx8eJFSpYsydChQ+nYsaMzkHr//v289957PukaFRVFp06dnIZaWFiYVyPeGON8oaSmpvLMM89w5swZ305KiPjzzz+z9XhK7iQ2NtZjVY7y5ctz8uRJfvvtN1JSUtzuz82bN9O+fXtOnTpFiRIluHjxIgMGDEBEOH36NKVLl6ZEiRJuXf82m41HHnkEESEhIQGbzQaQrusUrLbkL+fOnePGG290etUKFy7M8ePH/ZajKDlJUIaaMaa0MaaGfeoZIp2C0YeaNWs6/9+xYwfXXpt5tpDatWt7HJm3c+dOvvzySxo0aMA999xDVFQU8+fP59Zbb3U7poOqVavStm1b6tat6/dX2y+//OK3cXe1Mnr0aETE2T2TL18+UlNTeeONN9J9nZ88eZIyZcqQL9/lammRkZHs2LGDfPny0alTJ+666y5at25NWFgYXbt2pWbNmlSrVo0jR44476nGjRuzbds2RIRffvnFp/sKrJx7tWvXdhpq4eHhzpeRJxz306FDh+jRowdTpkxJt01iYmLAQd2ZsXfv3iyRq+QdkpKSaN68OVu3buWnn35ydlWKCOXLl+f48eMMHDiQ+fPnuw2U2bNnD/feey+7dl0e+L9gwQKnYWSMwRjj9kF16NAhbrrpJubOncu3337Ltm3bAFixYgUi4pwfP348f/75J9u3b3dLSp0ZkZGRdO3alS1btjh1cDB69Gh/T42i5AgBG2rGmLHAUmAC8D3wRIh0ChrHgyAuLo4GDRpkur2j9mdaFixYwFtvvUXbtm1p164dK1as4Ny5c5QqVYp8+fJRsmRJj/LCwsKcL+PExESfupMaNmzoFkuhWHgKPr548SKxsbG89dZbzmU2m418+fKxYMECt21TUlK48cYbKViwIImJic5tU1JSnN3iDz74IE2bNqVr167OZTVq1HDreixSpAgXL15k4sSJ3HfffYSHh3vVOW05s0KFCjmP7WDixInMnDkz3b7FihUjPj6eyMhImjZt6jE34Pbt21m8eLHX4ztYt25dptukZf/+dOkRlauMPXv20KlTJ2bNmkX58uWdYQXnzp2jVq1abNmyhaZNm7JgwQK6du3KgAEDSExMJCUlhUaNGlG5cmXAev62bNkyQ0/Ytm3bePTRR9m6dSuJiYlERUWRnJzM6dOniYmJYezYscTExJAvXz7eeustRo8ezahRozzK8pTbcvfu3TRo0MDNQHO8HyZMmKBVZJQ8QTAetZIi0lhE/iUinYDnQqVUsCQmJlKkSBGefPJJChcunOn2Du+JK47G7HhxFylShHPnztGoUSPAGojQqVMnrzId+3///fcsW7YsUx1cY+mUy3z11Vdu86dPn+aGG25g9erVnDt3zmnInDhxgkceeYR9+/YBllcgf/78vPXWW9SqVYsaNWq4deUcP37czYgPCwujTJkyzvlq1aqlG4SQmJjIxYsXqV+/PmXKlPHYhZmQkMCAAQMAiI+Pd6ZtsdlsznvC0c3jMAQPHjzoTBFTpUoVoqOjOXToEDVq1KBy5crpAru3bdtG3bp1Mz1348aN86mbZ926dc6PhOTkZO2Cv0LxVrc2bSjBjh07aNSoEWFhYdx6662EhYWRmprKqVOnKF++PJGRkTz//PMUL16cpk2b8tBDDzFz5kzCw8MpWLAgPXv2BKyPz+eee469e/d6TUZ+5MgRqlatyn/+8x/+7//+j9jYWI4dO0ajRo3YuHEjSUlJzJw5kwcffBBjDIMGDaJSpUoeZU2ePNn5/5w5czh79qwzabXjNzhISEigZcuWrF+/3p9TqOQBLl68SLdu3XJajZASjHWw3RhTzGW+dLDKhIpixYpRpUoV7r33Xp+2DwsLS/dy2rhxI82bN3db9tprr3HLLbcA0KpVK6pWrepVZtWqVTl48CA2my3THGunTp2iTJkyHvW42knrZdy5cyddunRh9uzZPPzww8yePZtSpUrx+OOP06JFC2ec17x582jfvj3FihXDGEONGjU4ePAgZ8+eBaBLly60aNHC63ELFy6c7voWLVqU++67D/DuhT127JjzGKtWrXLGRObPn5/Fixdz3XXXObd1dAPNnj3b2Z1TpUoVjh07hs1mIywsjNtuu425c+cCMGbMGI4dO0ZCQgINGjRI93GRlooVK7J8+fIMtwGr0samTZu4dOkSpUqVSuf9U64MevfunW7ZyZMnGTlypNuy48ePU758eb777juMMbRq1Yr169cTGxtLuXLlOHbsGE2aNOGjjz4CoFatWvz666/861//Aqx7HeA///kPtWrVYuvWrdSvX9/tGOvWrWPgwIEcPnwYYwwNGjSgWbNmgPXhctNNN7FkyRLq1KlDYmJiug9uEXG7Ty9duuTW5Tp37lxWrFjhNM6uvfZadu7cCUCJEiXYtGkT999/v4abXGH06tWLBQsW0KpVK48DzPIqwRhqzwAnjDH7jTH7gRyv++mgbt261KpVy699RMQtv86aNWs8Djzwlfbt29O3b19uvvnmTLeNioqiQYMGTm/KpUuXeP3111m6dGnAx8/LHDp0yO0h7Bp8vHv3bm666SZWrVrFQw89xN69e7n77rupW7cuYWFhzi7qyMhIt5dD9erVWbJkCX369KFp06bcdNNNmdbxTBuH9sILLzhTvdSrV8/jMH+HN2D48OEcO3bM6bW79957+fXXX2nfvr1z2zJlynDmzBmn9w8uG2oOg7148eJcuHCBDRs2UK1aNX7++WdiY2O5+eabnV09K1as8Kh/pUqVfM4GLyKcPHmSOnXqcOHChYC6TbOb2bNn57QK2ULarnR/mTt3LsnJyR5Tv/zzzz9u96Qnrr32WiIjIzl16hRl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pQrx8fEcOXKEm2++mb179zpTqzjSyjhGnDk8AImJic5uya1bt3L99dc747Tat2/Pb7/9xo033sjmzZt58cUXadmyZbpjlyhRwtkV6Mi/FxMTQ5UqVVi3bh0333wzBw4ccHaZO9qNa1deWFgYd911V6bpODxx00030bhxY7/3y26ee+45v7vny5UrR61atXjkkUfo3LkzgFtC6+joaCpVquQ0Ijyl9UlISHCO6q5bty533XWX1wTDine2b9/OuHHjuPfeezl+/DiLFy/mqaeeIjY2FhEhMjKSxMREdu3axf79+2nWrFm6soEOwsPDSU1NxRjjzFHqaBc2m401a9bQvn37DEdg79mzhzp16jhjTtetW5ft9ZchCI+aiFwAPnDMG2PaAMHUQDoBuLaAEvZlaY87ChgF0LJlS8koaWluIyUlhfHjx1OpUiXatWtHpUqVMMZ4Tby6fv36dOtWr15NeHi423JHfqPU1FSGDx/Oli1bvJZZSU1NZd++fRkme02LL7FhIsKlS5ecgcibN2+madOmbNu2DZvNRsmSJWnfvj0iwueff86QIUOoWLEi9957L7///judOnVyJpN18Mgjj/ilZ07QoEEDxo8fT9euXZ0v7OykWbNm3Hbbbdx3333ky5ePhg0b0rZtW6exn5YmTZrw73//O8PKBvny5aNdu3bExMRw+vRp5zXYu3cvzZo1o2TJkjn60o6Pj6dhw4a0aNGCtWvXsn79egoVKuT1Xjlx4gRhYWHpYo/8ZenSpdxzzz0sXbqUAgUK8H//939s3bqVmjVr0qJFCxYsWMA///xDt27dnCPPNm3axIIFCyhUqBClSpUiKSmJBx98kHXr1hEfH88tt9zCddddR61atZg6dapPsYlgJVMODw+nQYMGzJkzhxtvvJHffvuNl156yWkgOkrXuRp+ub095RYcsWbNmzdn7dq13HLLLURGRlK9enUqV67svPcc53Px4sWULl2amjVr8sILL7B48WIqVqzotR0qntm0aRNjxoyhYMGCHDlyhJSUFJ555hkeeOAB/v77b1atWkVUVBRPPfUU+/bt47PPPmPHjh1e7+u//vqLdu3akZCQQNmyZWnUqBE2m41Nmzbx3nvvERMTQ3h4OBUrVvSYwH7NmjU0bNiQokWL0qhRI5o0aUKBAgWcMrxlHwg1fnvUjDHd7X/HuU7AkCB1WQnU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PXva9wUFBREeHk6bNm246667rlp6sWLFimRNeN+lSxcmTZrEnXfeSZMmTbySq69pi1oh2Lx5s0ProbmifPnyOZqPHZGamsrYsWPp3r07bdu2ZdSoUcV+nBW4dlduq1atWL9+PU8//TS1a9f2ynJKV/rnP//p9deIiYmxD/T9888/mTx5Mn/88YdLEza/9NJLrFq1iunTp9O1a1eHnyciVKhQgaCgIBo3bpzvsVFRUVy4cIHjx487tEB8VFQUtWvXtrd2uGPRokX079+fGjVq8J///MfetbV06VJKlCjBc889R5cuXXjrrbdo1KgRgwcPZsaMGfbF7Hv27Mlzzz3HK6+8wvHjx3n33XeJioqiT58+DB069KrXa968+VWzxLvj3LlzV21bsWIFr776KgMGDKB3795069aNcePGcf78eVatWkXlypXtPQTh4eEebU0+ceIEvXr14sUXX6RXr16sXr06x/6ff/7ZoYXAr7R792772MbsLl68SP/+/Rk7diyHDh0iPDzcvi8qKsrloSUHDhygTp06VKpUiZ07d5KUlESZMmWoU6cOw4cPdylmYbtw4YJ9iShvMMawYsUK7r333jyPKWh97BIlStCnTx+2bNnCgQMHmDNnjqfT9Dkt1Lxs06ZN1KpVy2vxAwIC2LFjB9OmTeOLL75g/PjxTJgwIddjFy5cyMSJE5kxYwbPPPMMkZGRNG7cmBdeeKHITwrqCa5OSDx48GCqVavGXXfdlWP805YtW3Idv3Py5EmGDx/O1KlTnX6tkydPEhMT49UbSObOncu5c+f4/PPPycjIYObMmTRo0ID169dTooTzjfAiwosvvshrr73m1M83MjKSWrVqMXbsWIdeN/vAe0fcf//9/PDDD5w9e5bXX3+do0ePOpzbiRMn+PHHH/noo48oVaoUISEh3H777bRv355p06Yxfvx4goKCcm05CQwMpEuXLsycOZOHH34YsLp0AwICuPfeexkwYAAdO3YEyLX1sWbNmvY1Fd2VkZGR65x3hw4dytF6GhYWRv/+/Xn77be5ePEijz32mH1f5cqVPT5OVkQICwvLdYmy5ORkoqKiSExMZN26dbz33nv25cYuXLhg71rMPkmwMQZjDJGRkbl2NYeFhTFw4EAuXLiQ4/Pj6l3dp06d4sSJE4SEhNCxY0cOHTrEI488AkC7du2IiIgoEncvLliw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\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "
", - "image/png": "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q65QoULO7Zs1a0bfvn39Po5rLNq0adP43//+hzGGChUqaJxaHrBr165sjamculxsNht//fUXYCVRdnhpHYba8uXLWbFiRUC6HT16lJo1a3L27FkAFi5cyNq1az1uO336dJYvXx7QcRQllBw6dMjj8uTk5GxDRd588002b95M2bJlWbNmjXP5vn37iImJYdCgQSxdujTk+mbHsmXLnP///fffbN++PVePryjBclEZai1atODo0aOkp6dTuHBhdu7cSVTUhZ9Yr149mjVr5rZPuXLlfPKoedpm5cqVtGvXzjlfsWJFypUrR5kyZYLuLlN8Z968eaxevdrj8lWrVjFkyBB+//33LOtPnjzJsGHDWLNmDb/++itgGWq1a9fGGENaWhpVqlRh6tSpzJs3z82T4DC8cmLr1q3ccMMNHDhwALDi1bzl2Tt37pxbmpl9+/b5dIxQEqhBqlxcvPvuux6feRUqVPB6/x47doy4uDh27drFgw8+yIYNG5zr9u7dS9OmTXn99dezVPs4f/58FlmhzHP55ptvOgeanTx5kh07doRMtqLkBheVoVajRg3efvttAKpWrcrWrVspVaqUc/3NN9/MVVddlWW/AgUKZPtgGD9+PD169EBESEhIoEKFCgAsX76cTp06OberVasWdevWpWXLlm5fk76waNEiHYzgI3/88Yfb/Llz59i9e3eW87d161ZWrlxJ9erV3SoMzJ8/nwULFvDNN9/QuHFjhgwZQps2bUhLS6NmzZrUrVuXIkWKcO7cOUqXLk1sbCxdunRxBlLv3r2bt956yydd4+Li6Nq1q9NQi4qK8mrEG2OcL5SMjAwef/xxTp486dtJCRG//fZbrh5PiUyOHTvmsSpHxYoVOXr0KD///DPp6elu9+e6devo3Lkzx48fp1SpUpw7d44BAwYgIpw4cYKyZctSqlQpt65/m83G/fffj4iQnJyMzWYDyNJ1ClZb8pfTp09z1VVXOb1qRYsW5fDhw37LUZS8JChDzRhT1hhTyz71CpFOwehD7dq1nf9v3ryZyy/POVtI3bp1PY7M27JlC59++imNGjXi9ttvJy4ujtmzZ3Pddde5HdNB9erV6dChA/Xr1/f7q+2HH37w27i7VBk1ahQi4uyeKVCgABkZGbzyyitZvs6PHj1KuXLlKFDgQrW0bdu2sXnzZgoUKEDXrl259dZbadeu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i3Pdr79S86+/SH/qKUY1b86hQ4d45513+P777zl9+jTly5fnoYce8uu3XIzs3LmTn3/+mVKlSpGRkcHZs2eJiYnx6dr4yvPPw9ChF+ZjYiA21lp++dDneZYRDKc7PUwszz0HV1wxlJiYmJAdX1EUJa/Yv38/EydO5IUXXqBUqVKA5bAZMWIEp0+fJioqitdff51vvvmG66+/nipVquSxxkHi+sCPjob09DxVxxizSkTa+LWTrxYdcCOWJ+0z4DBWotvHgaL+Woehmvz1qDnI7Lk6fvy4fPHFF8753377Tb766iuP8ThTpkyx4jqy8bc7A1iNEdtzz0nKU085vXAZUVEhicW6mElKSnJ2gTqKS4e6S9TX7pLoaP8LaSuKokQyycnJMmjQIFm+fLmIWBUedu3aJWfOnJGhQ4fKqVOnInOwQKDkVXZbDxCARy3KD5vuW2AZUAi4XkQ6isg4ETnnl2UYAdSqVcttvly5cjRq1Ii4uDgSEhI4d+4cL774IpUrV2bRokXs2LGDl156ibS0NPbs2UPdunUtF4yI5Y5xQbAKnxoAEcywYRQePdrpfYt69llKly6dC78y/1KsWDEKFiwIQJEiRYiJiaF///4kJiY6tzl9+jS///57wMeIjc1y6bJgDNx88x7at28f8HEURVEijaJFi9KjRw82bNjAZ599RvXq1albty4lS5bklltuYeTIkTzyyCN5rWboiI21PGmxsXmtSUD43PVpjJkAPCMiKeFVyXcC6fr0RlJSEj/++CMpKSk89thjFClSBIC///6bc+fO0aZNGz799FO6detGp06dvAvK3K/miqOPTfGbc+fOMWTIEFq2bEmXLl3o27cvHTt25MyZM9x1110By23WDDZutP43Bp57zrpEGzZsIDk5me3bt/Pwww+H6FcoiqJEFjabjagof3w2SjAE0vXpj6FWXESSAtIsTITSUAMYMGAAJUuW5Nlnn/W4fvXq1bRq1cp3ga5WgBppIWHt2rWMHz+enj17UrlyZZYtW8batWs5ffo099xzD5dddlnQx5g5cyYnT56kVKlS3HzzzSHQWlEURVHCbKhFIqE21EaNGsV9992nXZP5EBGhb9++vPXWWxw/fpyDBw/SokULv+WcOnWK7777jueeey70SiqKoiiXNGqoKZc0Bw8eZMiQIdSvX59z585RtGhRihUrRpEiRbj1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V67s9OBt5TnOrnFXnGVkZDg0niQyMpL27duTlpbGV199lWvfiRMnqFy5MlWqVHHoNY8fP86uXbs4fPgw119/PX/72994+OGHadCgASdOnKBbt27UqFHDfkdafi3XgYGBtGnThrFjx+ZbQPqjAwcOcP311wNwyy230KlTJ+bPn+/jrCw//vgjEyZMsF80rwZxcXG53puRkZEu331sjOG9997LVVjPnj27xE1S3qNHjzznD728UJs9ezaDBw9m8ODBTJw4kTlz5vDf//6XzMxMvv76a0aNGsULL7zAwIEDGT9+PIsXL2br1q1+cz54kxZqRcAYQ0ZGhlcWrr7mmms4ceKES8/dtGlTrgkJlfOy5wM6cOCAw2OziqsjR444PL1Mo0aNuOuuuwgJCWHdunX27fPnz+fBBx90+DVr167Njh077K0cYWFh9sJl0qRJlC1blgcffJCZM2eSnp6eq7C5XNu2benTpw8hISGMHz++yO7o++OPP4iPjyc1NZVJkybl6ro5evQo58+fz/e5v/zyS65WjBo1ahAXF+fNdO1yTsdwuaNHj3Ls2DEef/xxn1woPfkzOHr0qH2ur8vXrrz++utd7jI/deoUt956K1u2bAGsaT82bNjA6tWr3U+6GMnvxqCyZcvae4MWLVpE3bp1KV26NEFBQQwdOpR77rkHgH79+tG8eXOGDBlCxYoVCQsLo3fv3kRERNCtWzeioqKu+rFuRX7XZ0m0fv16brrpJq/Erly5skszuKelpfHTTz8hIjRq1MgLmZUMUVFRHDhwgDVr1pCens5bb73l65S8pqAiKD+dOnVi8uTJVK5cmaioKIKCghweqykihISE8Msvv9C3b98r9mcvxxYQEEDv3r2ZN28eoaGhtG3bNt+YpUuXpk2bNlx33XV8+OGH9OvXz6nvJy9xcXF8+umnXHvttTz22GO59i1fvpxjx44RGhrK/v376dSpE8OHD+epp56iatWqzJ07l7CwMDIyMjDG0L59exo0aMCECRPIysri2LFjV8SsVKkS586dIzIy0u3c85Oens6ECROoWrUqISEhlC5dmh49elCunDUv+eLFi3n22WcJCgoiIcGzU14uWrSIo0ePkp6eTkJCAi+88EKuuwYPHDjAhAkTGDt2rFNxV61axQ8//MDLL79sn24ie/uhQ4do2rQpf/75JzfeeKN9X5UqVVye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j7Jw4UJ69erFwoULr5h8siBr1qyhf//+ALRu3ZrSpUuzbt06Nm7cyKBBg3j66aftBWvOOZTAKnIGDBhQ4MSjzmjfvn2uxyJS4ADz7Avj5fNlAWzfvp3p06dTrlw5MjIyCpzR/69//SvffvstqampHD58mC5dutgvgD///DOxsbG0a9eOV199Nc88VcmSc94qT8p5kapevTq7du2yDynYs2cPERERnDx5Mte5XdCHyex9tWrV4osvvuCOO+5wOJcaNWqwcuVKHnnkkVzbnZlmpmfPnnzyySccOXKE9957L88uwTFjxtiHmDg6uXlkZCTnzp3DGIOI8Msvv7B161YiIyPdvlFo//79JCUlsWbNGq677jp7d3F+EhMTCQsLIyYmxiOFuj+pV6/eFd3pXbt25e233yYoKIghQ4bkuomudOnSDBo0iIkTJxIREUGHDh349ddfadiwIXFxcQwbNoxrr72WypUrs3r1atLS0ti7dy9Dhw7lzTffJCYmhoSEBP7v//6P+vXrM2HCBF588UVOnjzp8vCiYl2ogXVxuueee5g1axYLFy4kJSWFFi1akJKSwooVKyhdujTt27enYsWKeY4byG88RP/+/XPNdlyqVCm6du3KDTfcYJ8l/YEHHmDBggU8+uij7Nq1y6V10PxR5cqVOXbsGA899BCrV6+mZcuWXh27tHr1au69916+++479u3bR7ly5ejWrRtTp07Nc6LTnLZv386KFStyfSIXEZo0aWK/ODjSohgREeH2jQTe0KRJE37++WeCg4OvuNhc7tprr2Xu3LmICEOGDOGjjz5i+fLlANx4441ev/tYKbDm4Mue965KlSq5WsdXr15Nz5497eMpJ0+eXOgceaGhoYBVqG3evNmpHot7772Xjh07OvcNXCYsLIwBAwawZ88e5s2bR3JyMqmpqQQGBhITE0P16tVp0qQJd999N+PGjXP4jluwWpTGjh1Lhw4d2LlzJ3379mXmzJlXLGVVkD/++IMpU6bw7rvvAlahPG/ePAYPHkxKSgrTp08vtCjfvn17ob+Hq0n2ON86derkOdNBUFAQL730EmD9PN9//30aNmzIrFmzmDhxIlu3bqVly5b247OL7caNGxMQEEBWVhY//PADCxcupHnz5sydO5cDBw7Y38vOKvaFGlg/9EceeQRjDGlpaU6dKPkpW7bsFdMmZP8ismUP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\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADNg0lEQVR4nOzdd3hUVfrA8e9JQu+9V2kiSJcmImtlXfVnWWUta0UUFXWt7FpQUUQsKL0j2FAsqIj03nsnhN4SIARCIKTO+/vjzgwzySSZmkzg/TzPPMncuffcMzP33nnvqUZEUEoppZRS4SeioDOglFJKKaU800BNKaWUUipMaaCmlFJKKRWmNFBTSimllApTGqgppZRSSoUpDdSUUkoppcJUvgdqxpjqxpjxxpi1LsuKG2OGG2P6G2MmGmOa5He+lFJKKaXCTUGUqF0LzACMy7IXgUMiMgj4HJhQAPlSSimllAor+R6oich0ICnL4tuAlfbXtwKtjDFl8ztvSimllFLhJFzaqFXFPXg7a1+mlFJKKXXZiiroDNidAMq4PC9rX5aNMeYp4CmAUqVKtWvWrFnoc6eUUkopFaD169fHi0gVX7YJl0BtJtAZWGqMaQlsFpGznlYUkbHAWID27dvLunXr8i+XSimllFJ+MsYc9HWbguj12R14GKhhjHnTGFMC+AKoZ4x5E3gZeCK/86WUUkopFW7yvURNRBYDiz289Gx+50UppZRSKpyFS2cCpZRSSimVhQZqSimllFJhSgM1pZRSSqkwpYFamJs3bx7XX399QWdDKaWUUgXgkg7Uli1bRo0aNZgzZ45z2d69e+natSvvvPMOAO+++y6jRo1i1KhRPPjgg27bX3PNNfz+++/Z0j1+/Dh33HEHffr0YcyYMTz00EMcP36c119/neuvv57x48czfvx4HnnkEbftHK+PGzeOfv36MW3atDzfw4033uj8f/LkyezZs8fjegcOHODRRx91Pn/zzTfzTNuTI0eOcMcdd3Dbbbc5l4kIHTt2pE+fPpw7d87rtIYMGUJiYiKABptKKaWUH8JlHLWQuPbaaylXrhw333yzc9kVV1xB48aN6dmzJwC//PILCxcupEKFCnTr1s253sKFC3nooYf47LPPuP32293SrVatGm3btqVZs2b06tWLffv2sXr1anr27EliYiJPPvkkH330EV999ZXbdo7X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12AqryoXm83GzJkzAWsQZUcprSNQW716NWvXrvUrbydPnqROnTqcO3cOgKVLl7Jp0yaP686ePZvVq1f7tR+lgunYsWMelycnJ+faVOT1119nx44dVKhQgY0bNzqXHzp0iL59+/LFF1+wcuXKoOc3N6tWrXL+/9dff7F79+583b9SgbqkArXWrVtz8uRJMjIyKFasGHv37iUi4uJbbNiwIS1btnTbpmLFil6VqHlaZ926dXTs2NH5vEqVKlSsWJHy5csHXF2mvLdo0SI2bNjgcfn69esZPnw4v/32W7bXT58+zahRo9i4cSO//PILYAVq9erVwxhDeno61atX588//2TRokVuJQmOwCsvu3bt4qabbuLIkSOA1V4tp3H2Lly44DbMzKFDh7zaRzD5G5CqS8ubb77p8ZpXuXLlHI/f+Ph4YmJi2LdvHw8++CBbt251vnbw4EFatGjBq6++mm22j9TU1GxpBXOcy9dff93Z0ez06dPs2bMnaGkrlR8uqUCtdu3a/Pe//wWgRo0a7Nq1i7Jlyzpfv/XWW7nmmmuybRcVFZXrhWHy5Mn069cPESEuLo7KlSsDsHr1arp27epcr27dujRo0IA2bdq43U16Y9myZdoZwUu///672/MLFy6wf//+bJ/frl27WLduHbVq1XKbYWDx4sUsWbKEr7/+mmbNmjF8+HDat29Peno6derUoUGDBhQvXpwLFy5Qrlw5RowYQffu3Z0Nqffv388bb7zhVV5jYmLo0aOHM1CLiIjIMYg3xjh/UDIzM3nsscc4ffq0dx9KkPz666/5uj8VnuLj4z3OylGlShVOnjzJTz/9REZGhtvxuXnzZrp168apU6coW7YsFy5cYMiQIYgICQkJVKhQgbJly7pV/dtsNu6//35EhOTkZGw2G0C2qlOwziVfJSYmcs011zhL1UqUKMHx48d9TkepghRQoGaMqWCMqWt/DAhSngLJD/Xq1XP+v2PHDq68Mu/RQho0aOCxZ97OnTv5+OOPadq0KXfccQcxMTHMnz+fG264wW2fDrVq1aJz5840atTI57u2H374wefg7nI1btw4RMRZPRMVFUVmZiYvvfRStrvzkydPUrFiRaKiLs6WFh0dzY4dO4iKiqJHjx7cdtttdOzYkYiICHr16kW9evWoXbs2R44ccR5TLVq0YNu2bYgIP/zwg1fHFVhj7jVo0MAZqEVGRjp/jDxxHE+HDh2iX79+TJs2Lds6KSkpfjfqzsvevXtDkq4qPNLS0mjbti1bt27lu+++c1ZVighVqlTh+PHjfP7558yfP9+to8yePXu488472bXrYsf/BQsWOAMjYwzGGLcbqkOHDtGlSxfmzp3LF198wbZt2wBYsWIFIuJ8PmnSJH799Ve2b9/uNih1XqKjo+nVqxdbtmxx5sFh3Lhxvn40ShUIvwM1Y8wEYCkwGfgKeDhIeQqY40KQlJRE06ZN81zfMfdnVgsWLOCVV16hc+fOdO3alRUrVpCYmEj58uWJioqiXLlyHtOLiIhw/hinpKR4VZ3UrFkzt7YUyuKp8fGFCxeIj4/nlVdecS6z2WxERUWxYMECt3UzMjK45pprKFasGCkpKc51MzIynNXi9957L61ataJXr17OZXXr1nWreixZsiQXLlxgypQp3HXXXURGRuaY56zTmRUvXty5b4cpU6bw559/Ztu2dOnSnD9/nujoaFq1auVxbMDt27ezePHiHPfvsG7dujzXyWr//mzDI6rLzJ49e+jRowezZs2iSpUqzmYFiYmJ1K9fny1bttCqVSsWLFhAr169GDJkCCkpKWRkZNC8eXNq1KgBWNff9u3b51oStm3bNh544AG2bt1KSkoKMTExpKenk5CQQFxcHBMmTCAuLo6oqCheeeUVxo0bx9ixYz2m5Wlsy927d9O0aVO3AM3x+zB58mSdRUYVCoGUqJUTkRYi8jcR6QE8EaxMBSolJYWSJUvy73//mxIlSuS5vqP0xJXjZHb8cJcsWZLExESaN28OWB0RevTokWOaju2/+uorli1blmceXNvSqYs++eQTt+cJCQm0bt2a1atXk5iY6AxkTpw4wf3338++ffsAq1SgSJEivPLKK9SvX5+6deu6VeUcP37cLYiPiIigYsWKzue1a9fO1gkhJSWFCxcu0KRJEypWrOixCjM5OZkhQ4YAcP78eeewLTabzXlMOKp5HIHgwYMHnUPE1KxZk9jYWA4dOkTdunWpUaNGtobd27Zto1GjRnl+dhMnTvSqmmfdunXOm4T09HStgr9E5TRvbdamBDt27KB58+ZERERwww03EBERQWZmJqdOnaJKlSpER0fz5JNPUqZMGVq1asU///lP/vzzTyIjIylWrBgDBgwArJvPJ554gr179+Y4GPmRI0eoVasW//jHP/i///s/4uPjOXbsGM2bN2fjxo2kpaXx559/cu+992KMYejQoVSvXt1jWt9++63z/zlz5nDmzBnnoNWO9+CQnJxM+/btWb9+vS8foSoELly4QJ8+fQo6G0EVSHSw3RhT2uV5hUAzEyylS5emZs2a3HnnnV6tHxERke3HaePGjbRt29Zt2QsvvMD1118PQIcOHahVq1aOadaqVYuDBw9is9nyHGPt1KlTVKxY0WM+LndZSxl37txJz549mT17Nvfddx+zZ8+mfPnyPPTQQ7Rr187ZzmvevHl069aN0qVLY4yhbt26HDx4kDNnzgDQs2dP2rVrl+N+S5Qoke37LVWqFHfddReQcynssWPHnPtYtWqVs01kkSJFWLx4MVdddZVzXUc10OzZs53VOTVr1uTYsWPYbDYiIiK46aabmDt3LgDjx4/n2LFjJCcn07Rp02w3F1lVq1aN5cuX57oOWDNtbNq0idTUVMqXL5+t9E9dGgYOHJht2cmTJxkzZozbsuPHj1OlShW+/PJLjDF06NCB9evXEx8fT+XKlTl27BgtW7bkzTffBKB+/fr8+OOP/O1vfwOsYx3gH//4B/Xr12fr1q00adLEbR/r1q3j888/5/DhwxhjaNq0KW3atAGsG5cuXbqwZMkSGjZsSEpKSrYbbhFxO05TU1Pdqlznzp3LihUrnMHZlVdeyc6dOwEoW7YsmzZt4u6779bmJpeY999/nwULFtChQwePHcwKq0ACtceAE8aY/caY/UCBz/vp0KhRI+rXr+/TNiLiNr7OmjVrPHY88Fa3bt0YNGgQ1157bZ7rxsTE0LRpU2dpSmpqKi+++CJLly71e/+F2aFDh9wuwq6Nj3fv3k2XLl1YtWoV//znP9m7dy+33347jRo1IiIiwllFHR0d7fbjUKdOHZYsWcKHH35Iq1at6NKlS57zeGZth9a7d2/nUC+NGzf22M3fURowatQojh075iy1u/POO/nxxx/p1q2bc92KFSty+vRpZ+kfXAzUHAF7mTJlOHfuHBs2bKB27dp8//33xMfHc+211zqrelasWOEx/9WrV/d6NHgR4eTJkzRs2JBz5875VW2a32bPnl3QWcgXWavSfTV37lzS09M9Dv3y119/uR2Tnlx55ZVER0dz6tQpKlWqxNVXX+3W5hOgf//+2YIxh3379rntwxjDihUreOmll3jmmWeyre9otxYTE0Pjxo2z9bA2xrBx40YmTJgAWCUoR48epWrVqoBVStelSxd27tzpvElu27at84e7S5cuTJ06lcaNG+dY0pf1hllnVSgczp07x/jx43niiSf8niIyHAUSqH0rIiVFpIGINMCanSAsXH/99VxxxRU+b9e/f39n6YVjBgN/VaxYkVOnTtGiRQuPY6udPn3aeTHYtm0bjRs3plmzZuzatYuYmBiuu+66Ah8csqDMmDGDzZs3k5qaSosWLdxKrtLS0ihevDi1atWiZMmSPP/8824llldffTUzZ87kjjvucEuzWLFiLFq0iIEDB9K5c2ev8vHCCy/k+Frp0qU9DtFx7Ngx7r//fsqUKcPDDz/srNKuWrUqI0aMAKwflhIlSlC3bl127Njh1taxbNmynDlzxu2YKV26NMuXL+fWW2/lpZde4oknnqBChQqcOnUKEWHKlCk5lsTmVUK7evVqmjVrhjGGuLg4GjZsSFJSEl9++aXH9UeOHJlrevlp6tSpBZ0Fv9hsNq968+7evZtz5845S64cfC11nzZtGkuWLHHeZLhKSkqiWrVqpKSksGbNGt577z3q1q3rtk6pUqVITk4mPj6eSpUq8dRTT2VL5+qrr85x/wMHDnSOTQjWueiouq9du7bbujabjYSEBBo3bszp06fp2LEjN910k9s6VatWZf78+Vy4cIHFixfz1FNPsW/fPurVq0dmZqazN/6OHTuczVNKlSrF+fPnAahXrx4bN26kRo0a2XqEg1Ut+vbbb7st83Z2kQ0bNlxSJTmFQVxcHAcPHkREaNCgAW+99Zaz48qlwu9ATUTeMMaUs8/3WdY+tVShVbp0ae6991527txJWlqaV23b8jJp0iSMMR6H//joo48YNWoUmZmZzrZMDRo0YO/evURHR9O5c+fLdiw2m83Gzp07OXXqFF26dCE6Oprp06ezaNEi5zq9e/f2uK0xho8++shjoP7ee++5DXicF9ehXTzx9IPpaMf40EMP5bhdamoqDRo0oG7duvzxxx9cffXVpKenU6RIEYwxHDx40Nl7GeDhhx/mueeeA6z3V7NmTQCuuuoqYmJiOHLkCPHx8aSlpTFv3jzndo4qX0eP5lOnTjnnMwWrNGXbtm387W9/o23btvz+++9cccUVnD592llN5CojI8OrqtT8cujQIa/Hswsn0dHRfP3118ye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nDlKAFJSUpzVklu3buXqq692ttPq1q0bP/30E9dccw2bN2/mqaeeon379tn2XbZsWWdVoGP8vbi4OGrWrMm6deu49tprOXDggLPK3HHeuFblRUREcNttt+U5HIcnXbp0oUWLFj5vl9+eeOIJn6vnK1euTP369bn//vu58cYbAdwGtI6NjaV69erOIMLTsD7JycnOXt2NGjXitttuy3GAYZWz7du3M3HiRO68806OHz/O4sWLeeSRR4iPj0dEiI6OJiUlhV27drF//37atGmTbdpAh8jISDIzMzHGOMcodZwXNpuNNWvW0K1bt1x7YO/Zs4eGDRs625yuW7cu3+dfhgBK1ETkHPBfx3NjTCcgkDmQTgCuZ0BZ+7Ks+x0LjAVo37695DZoabjJyMhg0qRJVK9ena5du1K9enWMMTkOvLp+/fpsr61evZrIyEi35Y7xjTIzMxk1ahRbtmzJcZqVzMxM9u3bl+tgr1l50zZMREhNTXU2RN68eTOtWrVi27Zt2Gw2ypUrR7du3RAR3nvvPYYPH061atW48847+fnnn+nRo4dzMFmH+++/36d8FoSmTZsyadIkevXq5fzBzk9t2rThpptu4q677iIqKopmzZrRuXNnZ7CfVcuWLfn73/+e68wGUVFRdO3albi4OBISEpzfwd69e2nTpg3lypUr0B/t8+fP06xZM9q1a8fatWtZv349xYsXz/FYOXHiBBEREdnaHvlq6dKl3HHHHSxdupSiRYvyf//3f2zdupV69erRrl07FixYwF9//UWfPn2cPc82bdrEggULKF68OOXLlyctLY17772XdevWcf78ea6//nquuuoq6tevz/Tp071qmwjWYMqRkZE0bdqUOXPmcM011/DTTz/x9NNPOwNEx9R1roFfuJ9P4cLR1qxt27asXbuW66+/nujoaOrUqUONGjWcx57j81y8eDEVKlSgXr169O7dm8WLF1OtWrUcz0Pl2aZNmxg/fjzFihXjyJEjZGRk8Nhjj3HPPffw+++/s2rVKmJiYnjkkUfYt28f7777Ljt27MjxuP7tt9/o2rUrycnJVKpUiebNm2Oz2di0aRNvvPEGcXFxREZGUq1aNY8D2K9Zs4ZmzZpRqlQpmjdvTsuWLSlatKgzjZxGHwg2n0vUjDHP2v9OdH0AwwPMy0qgnjHG0RCiKzAzwDTDSlRUlFv3+AYN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W7ZsyTZ80RtvvEGlSpWcgyWXL1+eY8eOOQs93n77bc6fP8/EiROJj4/nwIEDzg4mtWvXZu/evR5nfPGGP23UutsHo73eGPO2MeZt4EWgjl85UHlq0qQJP//8c7YG3AkJCezatYtatWqRlpbm/CGpUqWKW7H7qVOnSEpKck4zVKNGDVasWMG8efO45557WLduHT/99BMXLlxwKz1wzHdqjOHIkSMcOnSI2NhYqlWr5pzk/P7773c2bAarVM1RJF2sWDFnNVvWEghX3s69qfLHPffck+vrjkntwerM4Fp96bBp0yY6dOhAq1atSE5OdqvC2LlzJ3feeSfR0dG0b9/e2ei9UaNGLF26lGHDhnHmzBkqVKjAFVdcwZYtW+jYsSOzZs3Ktf1dREQE//rXv5y9mrPeva9YsYInnniCPXv25JhGWloac+fOZcWKFYwePZpt27bRsWPHXHvwVapU6bKd7u1S5rjxdHDUVGR93TGAscpZeno611xzDRs2bOC3337jxhtvZM6cOZw6dYqqVasSERHBjh07gjrQfFb/+9//PC53nZKtaNGipKens3r16jxLyBs1asQbb7zhbAa0YMEC9u/fz2uvvca4ceOYNm0aZcuW5fz584gIDzzwgHPmGF/5U6J2BjgAJAIH7Y89QM4zWKuAlC9fngULFmQLaGrXrs369etp1aqV2/gxlStX5siRIxQvXpzixYvz3Xff8euvvzJ16lSefvppatSowddff83jjz/uLK2Li4tzDq7pKLI/ceIEVatWpXz58uzcuZMHHniAYcOG0alTJ+rUqcPixYv5xz/+wZYtW5zjut16663ONjm33nprrpM1q0uD4852x44dzlKEli1b0qxZM6Kionj99deBiz9sjul/0tPTueqqq5yBXt26dZk2bRq33Xab8wexUaNGLF++nM6dOzNlypQ8h5aoX78+V111FR9//DFjxoxxm0R+586dNG/ePFvJ7oQJE5wjlO/fv5+xY8eyevVqbr75ZoYMGcLNN9/M9u3bc6yer1ixoo6XdYly3KxC9htgsI79cJqxI1zt27ePJk2aEBkZyd69e2nZsqXbrCIVKlRgz549zim+QiGnzjpZS9pEhJSUlDxL7Jo1a0bv3r1JTk4mKSmJOnXqsGzZMpo1a0avXr345z//Sd26ddmxYwdly5YNqE2qP23UNovIV8BTIvKV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fUefPn0oUaIEHTp0YMWKFRhjHO56PnLkCC1btiQuLo6LFy8ye/Zsxo8f73Zz/7Zt21i4cCEvvfQSt912G2B9joYOHUqVKlUQEYYOHerWazirVKlSTs1a3q5dO6fHgkZERNCrVy/Kli3LgAEDaN68OWXKlGHOnDmcOXOGmTNn2udpy8jIYNGiRSxevJi+fftSv359n602Ur58eaZPn87ly5eZO3cuZ86cISUlhYceeojRo0df1Qr066+/MmrUKCZPnszXX39t379161YGDBhw1d+LBx54INfXze38yCrUcpPffHq52bNnD/Xr189zf9b5VatWLaKioujYsSMiQpcuXfjiiy84ffo0f//731m8eDEVKlTg999/B2Dt2rX2z3XTpk3Zvn07s2fPZuDAgV4ZlhIcHEytWrXy3O/p7mtvuHDhgtfH0l28eNErXavh4eHUr1+f+fPnF4mWy4JooVaA9PT0XCfgW7p0KUeOHLlqe2pqKuPHj2f06NGUL1+eChUqFEKWOYWHh3Pw4EG++uor1qxZQ0RERIEXlPr163tkvI4vZWZmMnLkSH744Ydc93/55ZdMnjyZb7/9losXLzJz5kw6d+5sn0Rx+/btNGrUyK0cqlWrxp9//klsbCyVKlXiqaeesheCWWtPZhUfgYGBBAYGsnr1aoYNG+bwexQROnbsyIwZM2jXrh19+vQhNDTUpZtKwBrM+8MPP9CrV69iPSmziFCzZk3atGlDpUqV+O6772jfvj3Tp09n4sSJDB8+nEaNGvHKK694rBXAVY888gi7du3iscceo0ePHtx+++20adOGihUr0rt3bz766COmTJnC6tWrSUxMZOXKlQwaNIiXXnqJ1q1b8+mnn9ovYBEREZw+fZpTp06RkZFBfHy8faC2I66cZgGs2egnTpxI9+7dC7xQJiYm8uabb/LJJ58we/Zs6tatm+tx69ev5+LFi5QtWxawCvOsFqno6GgaNmxIx44dCQoK4vjx43Tt2pW9e/cC1mc8a0xdkyZN2LRpExkZGbm2HLorOTm5wBbNChUq+O20SsYY0tLSCuUzfuHCBZo1a+aV2O3ateP6669n5cqVAFy6dInp06fnuAs9uzNnzvD999/nO+Guz2T13RfF/5o3b268ITMz0/7vMWPGmHfeecfEx8fnOGb48OFm+vTpVz133Lhx5uzZs17Jy1lnz541W7ZsyfF+8rN27VozYsQI89tvv3k5s6tlZGS4HWPmzJnm8OHDZvz48Tne86RJk8z27dvNwoULjTHW7+i9994zSUlJxhhj1q9fb7Zs2WImTZrkdg7nzp0zU6ZMMRMnTjQJCQnGGGOWLFliPv/8czNx4kQTGxub4/jff//ddOvWzcyfP9/s2bPHrF+/3hw+fDjX2ElJSebTTz/NdV9GRoYZO3asw3muXr3apKenm4SEBPPhhx+aU6dOOfxcVXRs377djBw50ly8eDHH9mXLlpnPP//crFq1yhhjzOXLl820adPMRx99lOfnLz9TpkzJ8fizzz4zSUlJZu7cuebMmTM59l15rq9cudLs27cv39jz5883r7zyivn999/tOecnLi7OpKenm8mTJ5tTp06Z2bNn59jfpUsX8+effxYYJ+v1nbF+/Xqzbdu2Ao+bNm2aSUxMdCq2Ny1ZssSMHz/ezJw50/Tt29ds3LjR66/53//+16Snp3v1NT788EPz+++/mw8++MAkJCSYFStW2K8FxhiTmppqli5dakaMGGF27NhhPvjgA3PhwgVjjPVZdfR66Og1DIgxTtY6RbpFLT4+3t607ajMzEwuX75sr56TkpJITU1lzZo1zJgxg/Hjx/Paa69x4sQJNm3aRLNmzRg6dCjjxo1j9erVxMfHM3z4cNq2bXvV0jXbtm2jdu3aXr1t3hmRkZE0a9bM4e6ZW2+9lcGDB7NkyRL7nFSmkJqN+/fvz7x58zh//jxjx461fwty1JYtWyhTpgzR0dG0atWKkSNHsnLlSmJjYwkJCWHYsGH2QbFPPPEEL730kn2tultuuYXFixd75L1GRERQq1YtbrrpJvu39QceeIBLly7x8ssvXzVDd506dRg2bBiPPfaY/SaBHTt28PHHHzNx4kRWrVpl/4a3bNmyPLukAgICCA0NtXc15fZejDH89NNPjB49mvT0dD766CMWLlzIww8/7JOWX+V9jRs3ZtCgQVd1L91///0kJydz++23A9b4rB49etCvXz+io6Pdft3k5GRKlSplb2HObsCAATm27d+/P99uQrAG5999993s2rUr11nur3TdddfZW65XrFhx1ZjDkSNH2sfwFkRESEpKKvDvQ1paGsYYdu3alWOKj7x07NjRq3PQ/ec//2H79u0OH//nn3/y4IMPEhQUxNixY2nVqpXXcsty1113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\n" + "text/plain": " BR GridEx GridREx CART COSMiK ExACT CREAM\n0 2491 10 10 10 4 6 6\n1 2492 10 10 10 8 6 6\n2 2493 10 10 10 9 6 7", + "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
BRGridExGridRExCARTCOSMiKExACTCREAM
02491101010466
12492101010866
22493101010967
\n
" }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": " index V model GridEx GridREx CART \\\n0 2016-03-04 00:00:00 410.0 518.750997 456.38289 441.865819 441.865819 \n1 2016-03-04 01:00:00 400.0 522.352090 456.38289 441.865819 441.865819 \n2 2016-03-04 02:00:00 395.0 525.129143 456.38289 441.865819 441.865819 \n3 2016-03-04 03:00:00 408.0 528.767307 456.38289 441.865819 441.865819 \n4 2016-03-04 04:00:00 406.0 528.091871 456.38289 441.865819 441.865819 \n.. ... ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 456.38289 441.865819 441.865819 \n644 2016-03-30 20:00:00 501.0 489.614882 456.38289 441.865819 441.865819 \n645 2016-03-30 21:00:00 518.0 490.609540 456.38289 441.865819 441.865819 \n646 2016-03-30 22:00:00 510.0 491.321955 456.38289 441.865819 441.865819 \n647 2016-03-30 23:00:00 511.0 491.970557 456.38289 441.865819 441.865819 \n\n COSMiK CReEPy \n0 462.073299 477.108816 \n1 460.069181 473.640591 \n2 457.718672 469.171813 \n3 455.242336 465.418572 \n4 454.052186 462.959830 \n.. ... ... \n643 451.642998 432.796454 \n644 452.503446 433.519832 \n645 452.375362 432.264649 \n646 452.801142 431.704527 \n647 454.179640 433.203885 \n\n[648 rows x 8 columns]", - "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.750997456.38289441.865819441.865819462.073299477.108816
12016-03-04 01:00:00400.0522.352090456.38289441.865819441.865819460.069181473.640591
22016-03-04 02:00:00395.0525.129143456.38289441.865819441.865819457.718672469.171813
32016-03-04 03:00:00408.0528.767307456.38289441.865819441.865819455.242336465.418572
42016-03-04 04:00:00406.0528.091871456.38289441.865819441.865819454.052186462.959830
...........................
6432016-03-30 19:00:00497.0487.521085456.38289441.865819441.865819451.642998432.796454
6442016-03-30 20:00:00501.0489.614882456.38289441.865819441.865819452.503446433.519832
6452016-03-30 21:00:00518.0490.609540456.38289441.865819441.865819452.375362432.264649
6462016-03-30 22:00:00510.0491.321955456.38289441.865819441.865819452.801142431.704527
6472016-03-30 23:00:00511.0491.970557456.38289441.865819441.865819454.179640433.203885
\n

648 rows × 8 columns

\n
" - }, - "execution_count": 24, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -275,11 +298,12 @@ "for e in extractors:\n", " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())\n", "\n", - "plot(2491, p['index'], p.model, p.COSMiK)\n", - "plot(2491, p['index'], p.model, p.CReEPy)\n", - "plot(2491, p['index'], p.model, p.GridEx)\n", + "b = 2493\n", + "#plot(b, p['index'], p.model, p.COSMiK)\n", + "#plot(b, p['index'], p.model, p.ExACT)\n", + "#plot(b, p['index'], p.model, p.CREAM)\n", "\n", - "p" + "pd.DataFrame(rules)" ], "metadata": { "collapsed": false, @@ -290,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": null, "outputs": [], "source": [ "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", @@ -306,7 +330,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "outputs": [], "source": [], "metadata": { @@ -347,17 +371,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "outputs": [ - { - "data": { - "text/plain": "V 454.232082\nmodel 454.416913\nCART2 55.474434\nCART5 39.939136\nCART10 37.642059\nCART25 36.371025\nCART50 39.203983\nCART75 40.454190\nCART100 43.945508\nCART150 47.059375\nCART200 49.097016\nCART250 51.038554\nCART300 51.182619\nCART400 52.651320\nCART500 52.874986\nCART600 53.924574\nName: mean, dtype: float64" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "import pandas as pd\n", "\n", From 13bbea376999e0bf48d5508a46c4eb659c5b7b17 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 2 Jul 2023 15:55:19 +0200 Subject: [PATCH 29/67] wip --- demo/DemoRegressionScaled.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index cf02cb48..133d3e91 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 38, + "execution_count": 1, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 2, "outputs": [], "source": [ "def plot(testB, x, pred, extracted):\n", @@ -58,14 +58,14 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 5, "outputs": [], "source": [ "def splitTest(data):\n", " b = bartels[(bartels.n == 2490)]\n", " data = data[(data.index >= b.t1.values[0])]\n", "\n", - " b = bartels[(bartels.n == 2495) | (bartels.n == 2501) | (bartels.n == 2506)]\n", + " b = bartels[(bartels.n == 2494) | (bartels.n == 2495)]\n", " idx = np.zeros_like(data.index, dtype='bool')\n", " for _, row in b.iterrows():\n", " t0, t1 = row.t0, row.t1\n", @@ -163,7 +163,7 @@ " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", "def cosmik(model, train, test, normalization):\n", - " COSMiK = Extractor.cosmik(model, max_components=10, k=125, patience=12, close_to_center=True,\n", + " COSMiK = Extractor.cosmik(model, max_components=4, k=125, patience=12, close_to_center=True,\n", " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test, 'default'), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", From 0df232b872999547e62c39c92921300370804c72 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 2 Jul 2023 16:27:26 +0200 Subject: [PATCH 30/67] wip --- psyke/extraction/hypercubic/hypercube.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 404430c5..9266b758 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -285,7 +285,8 @@ def remove_duplicates(points: Iterable[Point]) -> Iterable[Point]: def split(point: Point, feature: str, n: int): points = [] - for value in np.linspace(self.get_first(feature), self.get_second(feature), n): + a, b = self.get_first(feature), self.get_second(feature) + for value in np.linspace(a, b, n) if n > 1 else [(a + b) / 2]: new_point = point.copy() new_point[feature] = value points.append(new_point) From 7c9490cec1cbe09d57de6bd608051f6aebf4443d Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 2 Jul 2023 16:28:10 +0200 Subject: [PATCH 31/67] feat: added midpoint-based vicinity --- psyke/extraction/hypercubic/hypercube.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 404430c5..9266b758 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -285,7 +285,8 @@ def remove_duplicates(points: Iterable[Point]) -> Iterable[Point]: def split(point: Point, feature: str, n: int): points = [] - for value in np.linspace(self.get_first(feature), self.get_second(feature), n): + a, b = self.get_first(feature), self.get_second(feature) + for value in np.linspace(a, b, n) if n > 1 else [(a + b) / 2]: new_point = point.copy() new_point[feature] = value points.append(new_point) From af837a00a981ebbf2d25e1ed0674eb40e2688f30 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 17 Jul 2023 04:07:59 +0200 Subject: [PATCH 32/67] [wip] --- psyke/extraction/hypercubic/__init__.py | 2 +- psyke/extraction/hypercubic/creepy/__init__.py | 3 ++- psyke/extraction/hypercubic/hypercube.py | 15 ++++++++------- psyke/utils/logic.py | 4 ++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 5becc59f..c0715076 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -122,7 +122,7 @@ def _ignore_dimensions(self) -> Iterable[str]: def __drop(self, dataframe: pd.DataFrame): self._hypercubes = [cube for cube in self._hypercubes if cube.count(dataframe) > 1] - def _create_theory(self, dataframe: pd.DataFrame, sort: bool = True) -> Theory: + def _create_theory(self, dataframe: pd.DataFrame, sort: bool = False) -> Theory: self.__drop(dataframe) new_theory = mutable_theory() for cube in self._hypercubes: diff --git a/psyke/extraction/hypercubic/creepy/__init__.py b/psyke/extraction/hypercubic/creepy/__init__.py index eb4a9584..1d72f759 100644 --- a/psyke/extraction/hypercubic/creepy/__init__.py +++ b/psyke/extraction/hypercubic/creepy/__init__.py @@ -11,6 +11,7 @@ from psyke.clustering import HyperCubeClustering from psyke.extraction.hypercubic import HyperCubeExtractor from psyke.utils import Target +from psyke.utils.logic import last_in_body class CReEPy(HyperCubeExtractor): @@ -40,7 +41,7 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort last_clause = list(theory.clauses)[-1] theory.retract(last_clause) theory.assertZ(clause( - last_clause.head, [list(last_clause.body)[-1]] if self._output is Target.REGRESSION else [])) + last_clause.head, [last_in_body(last_clause.body)] if self._output is Target.REGRESSION else [])) last_cube = self._hypercubes[-1] for dimension in last_cube.dimensions.keys(): last_cube[dimension] = [-np.inf, np.inf] diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 9266b758..fcc2a063 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -379,14 +379,15 @@ def copy(self) -> RegressionCube: return RegressionCube(self.dimensions.copy()) def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, normalization=None) -> Iterable[Struct]: - intercept = self.output.intercept_ if normalization is None else \ - unscale(sum([-self.output.coef_[i] * normalization[name][0] / normalization[name][1] for i, name in - enumerate(self.dimensions.keys())], self.output.intercept_), list(normalization.keys())[-1]) - coefs = self.output.coef_ if normalization is None else \ - [self.output.coef_[i] / normalization[name][1] for i, name in enumerate(self.dimensions.keys())] + intercept = self.output.intercept_ if normalization is None else unscale(sum( + [-self.output.coef_[i] * normalization[name][0] / normalization[name][1] for i, name in + enumerate(self.dimensions.keys())], self.output.intercept_), list(normalization.keys())[-1]) + coefs = self.output.coef_ if normalization is None else [ + self.output.coef_[i] / normalization[name][1] * normalization[list(normalization.keys())[-1]][1] for + i, name in enumerate(self.dimensions.keys()) + ] return list(super().body(variables, ignore, unscale, normalization)) + [linear_function_creator( - list(variables.values()), [to_rounded_real(v) for v in coefs], - to_rounded_real(intercept) + list(variables.values()), [to_rounded_real(v) for v in coefs], to_rounded_real(intercept) )] diff --git a/psyke/utils/logic.py b/psyke/utils/logic.py index d606252d..e56da90a 100644 --- a/psyke/utils/logic.py +++ b/psyke/utils/logic.py @@ -134,6 +134,10 @@ def create_variable_list(features: list[DiscreteFeature], dataset: pd.DataFrame return values +def last_in_body(body: Struct) -> Struct: + return body.args[-1] if body.args[-1].functor == 'is' else last_in_body(body.args[-1]) + + def create_head(functor: str, variables: Iterable[Var], output) -> Struct: if isinstance(output, Var): variables += [output] From 70240d1661457798dbdb7f55ed6e9a53e5d68f86 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Wed, 12 Jul 2023 18:49:19 +0200 Subject: [PATCH 33/67] tests: [wip] --- psyke/extraction/hypercubic/__init__.py | 2 +- psyke/extraction/hypercubic/divine/__init__.py | 2 +- psyke/extraction/hypercubic/hypercube.py | 1 + psyke/utils/plot.py | 2 +- test/psyke/extraction/hypercubic/test_hypercube.py | 4 ++-- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index c0715076..12721d63 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -54,7 +54,7 @@ def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, def _create_brute_tree(self, criterion: str = 'center', n: int = 2) -> (BallTree, list[GenericCube]): points = None if criterion == 'center': - points = [(cube.center(), cube) for cube in self._hypercubes] + points = [(cube.center, cube) for cube in self._hypercubes] elif criterion == 'density': points = [(cube.barycenter, cube) for cube in self._hypercubes] elif criterion == 'corner': diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index d5674776..08c80e74 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -50,7 +50,7 @@ def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFra @staticmethod def closest_to_center(tree: BallTree, cube: GenericCube): - return tree.query([list(cube.center().dimensions.values())], k=1)[1][0][-1] + return tree.query([list(cube.center.dimensions.values())], k=1)[1][0][-1] @staticmethod def closest_to_corners(tree: BallTree, cube: GenericCube): diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index fcc2a063..b11643ba 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -259,6 +259,7 @@ def diagonal(self) -> float: lambda a, b: a + b, [(dimension[1] - dimension[0]) ** 2 for dimension in self._dimensions.values()], 0 ) ** 0.5 + @property def center(self) -> Point: return Point(list(self._dimensions.keys()), [(interval[0] + interval[1]) / 2 for interval in self._dimensions.values()]) diff --git a/psyke/utils/plot.py b/psyke/utils/plot.py index 04be6b96..fafbd346 100644 --- a/psyke/utils/plot.py +++ b/psyke/utils/plot.py @@ -60,7 +60,7 @@ def plot_perimeters(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[ def plot_centers(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): for cube in extractor._hypercubes: - center = cube.center() + center = cube.center plt.scatter(center[x], center[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) diff --git a/test/psyke/extraction/hypercubic/test_hypercube.py b/test/psyke/extraction/hypercubic/test_hypercube.py index b241029c..ef4fcec4 100644 --- a/test/psyke/extraction/hypercubic/test_hypercube.py +++ b/test/psyke/extraction/hypercubic/test_hypercube.py @@ -225,8 +225,8 @@ def test_diagonal(self): self.assertEqual(self.cube.diagonal(), ((self.x[1] - self.x[0])**2 + (self.y[1] - self.y[0])**2)**0.5) def test_center(self): - self.assertEqual(self.cube.center(), Point(list(self.dimensions.keys()), - [(val[0] + val[1]) / 2 for val in self.dimensions.values()])) + self.assertEqual(self.cube.center, Point(list(self.dimensions.keys()), + [(val[0] + val[1]) / 2 for val in self.dimensions.values()])) def test_corners(self): self.assertEqual(self.cube.corners(), [ From df5ebac7711ba89f6aba90cf1934e4a19d67c227 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 5 Jun 2023 01:57:55 +0200 Subject: [PATCH 34/67] feat: added divine extractor --- demo/DemoClassification.ipynb | 406 +++++------------- psyke/__init__.py | 9 + psyke/extraction/hypercubic/__init__.py | 8 +- .../extraction/hypercubic/creepy/__init__.py | 6 +- .../extraction/hypercubic/divine/__init__.py | 70 +++ psyke/extraction/hypercubic/hypercube.py | 37 +- 6 files changed, 219 insertions(+), 317 deletions(-) create mode 100644 psyke/extraction/hypercubic/divine/__init__.py diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index dfb35697..6d076555 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -47,7 +47,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "id": "f8e46c49", "metadata": {}, "outputs": [], @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "id": "38d5afb0", "metadata": {}, "outputs": [], @@ -85,7 +85,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "id": "4f807185", "metadata": {}, "outputs": [ @@ -94,7 +94,7 @@ "text/plain": " target\n0 setosa\n1 setosa\n2 setosa\n3 setosa\n4 setosa\n.. ...\n145 virginica\n146 virginica\n147 virginica\n148 virginica\n149 virginica\n\n[150 rows x 1 columns]", "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
target
0setosa
1setosa
2setosa
3setosa
4setosa
......
145virginica
146virginica
147virginica
148virginica
149virginica
\n

150 rows × 1 columns

\n
" }, - "execution_count": 7, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -115,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "id": "7ac49b4e", "metadata": {}, "outputs": [ @@ -124,7 +124,7 @@ "text/plain": " SepalLength SepalWidth PetalLength PetalWidth iris\n0 5.1 3.5 1.4 0.2 setosa\n1 4.9 3.0 1.4 0.2 setosa\n2 4.7 3.2 1.3 0.2 setosa\n3 4.6 3.1 1.5 0.2 setosa\n4 5.0 3.6 1.4 0.2 setosa\n.. ... ... ... ... ...\n145 6.7 3.0 5.2 2.3 virginica\n146 6.3 2.5 5.0 1.9 virginica\n147 6.5 3.0 5.2 2.0 virginica\n148 6.2 3.4 5.4 2.3 virginica\n149 5.9 3.0 5.1 1.8 virginica\n\n[150 rows x 5 columns]", "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
SepalLengthSepalWidthPetalLengthPetalWidthiris
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa
..................
1456.73.05.22.3virginica
1466.32.55.01.9virginica
1476.53.05.22.0virginica
1486.23.45.42.3virginica
1495.93.05.11.8virginica
\n

150 rows × 5 columns

\n
" }, - "execution_count": 8, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "03fc5e2c", "metadata": {}, "outputs": [], @@ -165,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "id": "aa8a3128", "metadata": {}, "outputs": [ @@ -173,14 +173,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Accuracy: 1.00\n", - "F1: 1.00\n" + "Accuracy: 0.97\n", + "F1: 0.97\n" ] } ], "source": [ "#predictor = MLPClassifier(alpha=1, max_iter=1000)\n", - "predictor = KNeighborsClassifier(n_neighbors=5)\n", + "predictor = KNeighborsClassifier(n_neighbors=7)\n", "#predictor = DecisionTreeClassifier()\n", "predictor.fit(train.iloc[:, :-1], train.iloc[:, -1])\n", "print(f'Accuracy: {accuracy_score(predictor.predict(test.iloc[:, :-1]), test.iloc[:, -1]):.2f}')\n", @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "outputs": [], "source": [ "def print_scores(scores):\n", @@ -210,140 +210,136 @@ } }, { - "cell_type": "code", - "execution_count": 12, + "cell_type": "markdown", + "source": [ + "We create an extractor that uses the CART algorithm and we extract prolog rules from our trained KNN." + ], "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 14, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "ITER performance (5 rules with 97.37% coverage):\n", - "Classification accuracy = 0.97 (data), 0.97 (BB)\n", - "F1 = 0.97 (data), 0.97 (BB)\n", + "CART performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", "\n", - "ITER extracted rules:\n", + "CART extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 3.17], PetalWidth in [0.09, 2.10].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [3.17, 5.82], PetalWidth in [0.09, 1.74].\n", + " PetalLength =< 2.6.\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 4.05], PetalWidth in [2.10, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [5.82, 6.90], PetalWidth in [0.09, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [4.05, 5.82], PetalWidth in [1.74, 2.50].\n" + " PetalLength =< 4.75.\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" ] } ], "source": [ - "it = Extractor.iter(predictor, min_update=0.15, min_examples=150, threshold=0.1, max_iterations=600, n_points=1)\n", - "theory_from_iter = it.extract(train)\n", - "scores, completeness = get_scores(it, test, predictor)\n", - "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "cart = Extractor.cart(predictor, simplify=True)\n", + "theory_from_cart = cart.extract(train)\n", + "scores, completeness = get_scores(cart, test, predictor)\n", + "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", - "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "We create a GridEx extractor to extract prolog rules from the same KNN." - ] - }, - { - "cell_type": "code", - "execution_count": 13, + "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, + } + }, + { + "cell_type": "code", + "execution_count": 16, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "GridEx performance (3 rules with 94.74% coverage):\n", - "Classification accuracy = 0.92 (data), 0.92 (BB)\n", - "F1 = 0.92 (data), 0.92 (BB)\n", + "DiViNE performance (3 rules with 73.68% coverage):\n", + "Classification accuracy = 0.96 (data), 1.00 (BB)\n", + "F1 = 0.96 (data), 1.00 (BB)\n", "\n", - "GridEx extracted rules:\n", + "DiViNE extracted rules:\n", "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 2.47].\n", + " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.21, 4.68].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.68, 6.90].\n" + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n" ] } ], "source": [ - "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", - "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "divine = Extractor.divine(predictor, k=5, patience=15)\n", + "theory_from_divine = divine.extract(train)\n", + "scores, completeness = get_scores(divine, test, predictor)\n", + "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ] + "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "it = Extractor.iter(predictor, min_update=0.15, min_examples=150, threshold=0.1, max_iterations=600, n_points=1)\n", + "theory_from_iter = it.extract(train)\n", + "scores, completeness = get_scores(it, test, predictor)\n", + "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "markdown", + "source": [ + "We create a GridEx extractor to extract prolog rules from the same KNN." + ], "metadata": { "collapsed": false - }, - "source": [ - "We create an extractor that uses the CART algorithm and we extract prolog rules from our trained KNN." - ] + } }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, + "outputs": [], + "source": [ + "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", + "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", + "theory_from_gridEx = gridEx.extract(train)\n", + "scores, completeness = get_scores(gridEx, test, predictor)\n", + "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 0.97 (BB)\n", - "F1 = 0.97 (data), 0.97 (BB)\n", - "\n", - "CART extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength =< 2.6.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength =< 4.75.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ] + } }, { "cell_type": "markdown", @@ -359,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "outputs": [], "source": [ "def print_clustering_scores(scores):\n", @@ -389,53 +385,8 @@ }, { "cell_type": "code", - "execution_count": 16, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.ExACT. Depth: 1. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.02. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.06. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.11. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.15. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.20. Predictive loss = 0.30, 2 rules\n", - "\n", - "Algorithm.ExACT. Depth: 2. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.02. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.06. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.11. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.15. Predictive loss = 0.27, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.20. Predictive loss = 0.27, 3 rules\n", - "\n", - "****************************************\n", - "* Best Algorithm.ExACT\n", - "****************************************\n", - "* Predictive loss = 0.27, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best Predictive loss\n", - "****************************************\n", - "* Predictive loss = 0.27, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best N rules\n", - "****************************************\n", - "* Predictive loss = 0.30, 2 rules\n", - "* Threshold = 1.00\n", - "* Depth = 2\n", - "****************************************\n", - "\n" - ] - } - ], + "execution_count": null, + "outputs": [], "source": [ "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.ExACT, output=Target.CLASSIFICATION,\n", " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", @@ -451,43 +402,14 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ExACT performance (3 clusters with 97.37% coverage):\n", - "ARI = 0.53\n", - "AMI = 0.62\n", - "V-measure = 0.64\n", - "FMI = 0.69\n", - "Classification accuracy = 0.78\n", - "\n", - "Output is virginica if:\n", - " SepalLength is in [4.90, 7.70]\n", - " SepalWidth is in [2.20, 3.80]\n", - " PetalLength is in [4.50, 6.90]\n", - " PetalWidth is in [1.40, 2.50]\n", - "Output is versicolor if:\n", - " SepalLength is in [4.90, 7.70]\n", - " SepalWidth is in [2.00, 3.80]\n", - " PetalLength is in [3.30, 6.90]\n", - " PetalWidth is in [1.00, 2.50]\n", - "Output is setosa if:\n", - " SepalLength is in [4.30, 7.70]\n", - " SepalWidth is in [2.00, 4.40]\n", - " PetalLength is in [1.00, 6.90]\n", - " PetalWidth is in [0.10, 2.50]\n" - ] - } - ], + "outputs": [], "source": [ "exact = Clustering.exact(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", "exact.fit(train)\n", @@ -502,32 +424,14 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.95 (BB)\n", - "F1 = 0.95 (data), 0.95 (BB)\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.79, 6.90].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.29, 6.90].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa).\n" - ] - } - ], + "outputs": [], "source": [ "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", " ranks=ranked, ignore_threshold=.99, clustering=Clustering.exact)\n", @@ -540,53 +444,8 @@ }, { "cell_type": "code", - "execution_count": 19, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.CREAM. Depth: 1. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.02. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.06. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.11. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.15. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.20. Predictive loss = 0.30, 2 rules\n", - "\n", - "Algorithm.CREAM. Depth: 2. Threshold = 1.00. Predictive loss = 0.30, 2 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.02. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.06. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.11. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.15. Predictive loss = 0.07, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.20. Predictive loss = 0.07, 3 rules\n", - "\n", - "****************************************\n", - "* Best Algorithm.CREAM\n", - "****************************************\n", - "* Predictive loss = 0.07, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best Predictive loss\n", - "****************************************\n", - "* Predictive loss = 0.07, 3 rules\n", - "* Threshold = 0.02\n", - "* Depth = 2\n", - "****************************************\n", - "\n", - "****************************************\n", - "* Best N rules\n", - "****************************************\n", - "* Predictive loss = 0.30, 2 rules\n", - "* Threshold = 1.00\n", - "* Depth = 2\n", - "****************************************\n", - "\n" - ] - } - ], + "execution_count": null, + "outputs": [], "source": [ "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.CREAM, output=Target.CLASSIFICATION,\n", " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", @@ -602,43 +461,14 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CREAM performance (3 clusters with 97.37% coverage):\n", - "ARI = 0.85\n", - "AMI = 0.82\n", - "V-measure = 0.83\n", - "FMI = 0.90\n", - "Classification accuracy = 0.95\n", - "\n", - "Output is setosa if:\n", - " SepalLength is in [4.30, 5.70]\n", - " SepalWidth is in [2.30, 4.40]\n", - " PetalLength is in [1.00, 1.90]\n", - " PetalWidth is in [0.10, 0.60]\n", - "Output is versicolor if:\n", - " SepalLength is in [4.90, 7.00]\n", - " SepalWidth is in [2.00, 3.40]\n", - " PetalLength is in [3.30, 5.10]\n", - " PetalWidth is in [1.00, 1.80]\n", - "Output is virginica if:\n", - " SepalLength is in [4.30, 7.70]\n", - " SepalWidth is in [2.00, 4.40]\n", - " PetalLength is in [1.00, 6.90]\n", - " PetalWidth is in [0.10, 2.50]\n" - ] - } - ], + "outputs": [], "source": [ "cream = Clustering.cream(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", "cream.fit(train)\n", @@ -653,32 +483,14 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.95 (BB)\n", - "F1 = 0.95 (data), 0.95 (BB)\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 1.90].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.29, 5.00].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" - ] - } - ], + "outputs": [], "source": [ "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", " ranks=ranked, ignore_threshold=.99, clustering=Clustering.cream)\n", diff --git a/psyke/__init__.py b/psyke/__init__.py index eb71036c..e8d1564a 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -218,6 +218,15 @@ def cart(predictor, max_depth: int = 3, max_leaves: int = 3, return Cart(predictor, max_depth, max_leaves, discretization=discretization, normalization=normalization, simplify=simplify) + @staticmethod + def divine(predictor, k: int = 5, patience: int = 15, + discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: + """ + Creates a new DiViNE extractor. + """ + from psyke.extraction.hypercubic.divine import DiViNE + return DiViNE(predictor, k=k, patience=patience, discretization=discretization, normalization=normalization) + @staticmethod def iter(predictor, min_update: float = 0.1, n_points: int = 1, max_iterations: int = 600, min_examples: int = 250, threshold: float = 0.1, fill_gaps: bool = True, normalization: dict[str, tuple[float, float]] = None, diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 7d7783a5..e0233eb7 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -17,8 +17,8 @@ class HyperCubePredictor: - def __init__(self, cubes=[], output=Target.CONSTANT, normalization=None): - self._hypercubes = cubes + def __init__(self, output=Target.CONSTANT, normalization=None): + self._hypercubes = [] self._output = output self.normalization = normalization @@ -50,8 +50,8 @@ def _get_cube_output(cube, data: dict[str, float]) -> float: class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC): - def __init__(self, predictor, output, normalization): - PedagogicalExtractor.__init__(self, predictor, normalization=normalization) + def __init__(self, predictor, output, discretization=None, normalization=None): + PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) HyperCubePredictor.__init__(self, output=output, normalization=normalization) def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: diff --git a/psyke/extraction/hypercubic/creepy/__init__.py b/psyke/extraction/hypercubic/creepy/__init__.py index 8bdba96f..eb4a9584 100644 --- a/psyke/extraction/hypercubic/creepy/__init__.py +++ b/psyke/extraction/hypercubic/creepy/__init__.py @@ -13,16 +13,16 @@ from psyke.utils import Target -class CReEPy(HyperCubeExtractor, ABC): +class CReEPy(HyperCubeExtractor): """ Explanator implementing CReEPy algorithm. """ def __init__(self, predictor, depth: int, error_threshold: float, output: Target = Target.CONSTANT, gauss_components: int = 5, ranks: list[(str, float)] = [], ignore_threshold: float = 0.0, - normalization=None, clustering=Clustering.exact): + discretization=None, normalization=None, clustering=Clustering.exact): super().__init__(predictor, Target.CLASSIFICATION if isinstance(predictor, ClassifierMixin) else output, - normalization) + discretization, normalization) self.clustering = clustering(depth, error_threshold, self._output, gauss_components) self.ranks = ranks self.ignore_threshold = ignore_threshold diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py new file mode 100644 index 00000000..fe1735d9 --- /dev/null +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -0,0 +1,70 @@ +import numpy as np +import pandas as pd +from tuprolog.theory import Theory + +from psyke import Target +from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube + +from sklearn.neighbors import BallTree + +from psyke.extraction.hypercubic.hypercube import Point, GenericCube + + +class DiViNE(HyperCubeExtractor): + """ + Explanator implementing DiViNE algorithm. + """ + + def __init__(self, predictor, k: int = 5, patience: int = 15, discretization=None, normalization=None): + super().__init__(predictor, Target.CLASSIFICATION, discretization, normalization) + self.k = k + self.patience = patience + + @staticmethod + def __pop(data: pd.DataFrame, idx: int = None) -> (Point, pd.DataFrame): + if idx is None: + idx = data.sample(1).index.values[0] + t = data.T + return DiViNE.__to_point(t.pop(idx)), t.T.reset_index(drop=True) + + @staticmethod + def __to_point(instance) -> Point: + point = Point(instance.index.values, instance.values) + return point + + def __to_cube(self, point: Point) -> GenericCube: + cube = HyperCube.cube_from_point(point.dimensions, self._output) + cube._output = list(point.dimensions.values())[-1] + return cube + + def __clean(self, data: pd.DataFrame) -> pd.DataFrame: + tree = BallTree(data.iloc[:, :-1], leaf_size=2) + _, idx = tree.query(data.iloc[:, :-1], k=self.k) + # how many output classes are associated with the k neighbors + count = np.array(list(map(lambda indices: len(data.iloc[indices].iloc[:, -1].unique()), idx))) + # instances with neighbors of different classes are discarded + return data[count == 1] + + def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: + data = self.__clean(dataframe) + + while len(data) > 0: + discarded = [] + patience = self.patience + point, data = self.__pop(data) + cube = self.__to_cube(point) + + while patience > 0 and len(data) > 0: + tree = BallTree(data.iloc[:, :-1], leaf_size=2) + _, idx = tree.query([list(cube.center().dimensions.values())], k=1) + other, data = DiViNE.__pop(data, idx[0][-1]) + if cube.output == list(other.dimensions.values())[-1]: + cube = cube.merge_with_point(other) + data = data[~(cube.filter_indices(data.iloc[:, :-1]))].reset_index(drop=True) + else: + patience -= 1 + discarded.append(other) + self._hypercubes.append(cube) + if len(discarded) > 0: + data = pd.concat([data] + [d.to_dataframe() for d in discarded]).reset_index(drop=True) + return self._create_theory(dataframe, sort) diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 7c8d2d2b..f122d104 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -30,25 +30,28 @@ class Point: EPSILON = get_default_precision() - def __init__(self, dimensions: list[str], values: list[float]): + def __init__(self, dimensions: list[str], values: list[float | str]): self._dimensions = {dimension: value for (dimension, value) in zip(dimensions, values)} - def __getitem__(self, feature: str) -> float: + def __getitem__(self, feature: str) -> float | str: if feature in self._dimensions.keys(): return self._dimensions[feature] else: raise FeatureNotFoundException(feature) - def __setitem__(self, key: str, value: float) -> None: + def __setitem__(self, key: str, value: float | str) -> None: self._dimensions[key] = value def __eq__(self, other: Point) -> bool: return all([abs(self[dimension] - other[dimension]) < Point.EPSILON for dimension in self._dimensions]) @property - def dimensions(self) -> dict[str, float]: + def dimensions(self) -> dict[str, float | str]: return self._dimensions + def to_dataframe(self) -> pd.DataFrame: + return pd.DataFrame(data=[self.dimensions.values()], columns=list(self.dimensions.keys())) + class HyperCube: """ @@ -59,7 +62,7 @@ class HyperCube: INT_PRECISION = get_int_precision() def __init__(self, dimension: dict[str, tuple[float, float]] = None, limits: set[Limit] = None, - output: float | LinearRegression = 0.0): + output: float | LinearRegression | str = 0.0): self._dimensions = self._fit_dimension(dimension) if dimension is not None else {} self._limits = limits if limits is not None else set() self._output = output @@ -101,7 +104,7 @@ def limit_count(self) -> int: return len(self._limits) @property - def output(self) -> float | LinearRegression: + def output(self) -> float | str | LinearRegression: return self._output @property @@ -128,9 +131,8 @@ def filter_indices(self, dataset: pd.DataFrame) -> ndarray: def _filter_dataframe(self, dataset: pd.DataFrame) -> pd.DataFrame: return dataset[self.filter_indices(dataset)] - def _zip_dimensions(self, hypercube: HyperCube) -> list[ZippedDimension]: - return [ZippedDimension(dimension, self[dimension], hypercube[dimension]) - for dimension in self._dimensions.keys()] + def _zip_dimensions(self, other: HyperCube) -> list[ZippedDimension]: + return [ZippedDimension(dimension, self[dimension], other[dimension]) for dimension in self._dimensions.keys()] def add_limit(self, limit_or_feature: Limit | str, direction: str = None) -> None: if isinstance(limit_or_feature, Limit): @@ -202,7 +204,7 @@ def _create_tuple(self, generator: Random) -> dict: return {k: generator.uniform(self.get_first(k), self.get_second(k)) for k in self._dimensions.keys()} @staticmethod - def cube_from_point(point: dict, output=None) -> GenericCube: + def cube_from_point(point: dict[str, float], output=None) -> GenericCube: if output is Target.CLASSIFICATION: return ClassificationCube({k: (v, v) for k, v in list(point.items())[:-1]}) if output is Target.REGRESSION: @@ -276,6 +278,15 @@ def merge_along_dimension(self, cube: HyperCube, feature: str) -> HyperCube: new_cube.update_dimension(feature, (min(a1, a2), max(b1, b2))) return new_cube + def merge(self, other: HyperCube) -> HyperCube: + new_cube = self.copy() + for dimension in self.dimensions.keys(): + new_cube = new_cube.merge_along_dimension(other, dimension) + return new_cube + + def merge_with_point(self, other: Point) -> HyperCube: + return self.merge(HyperCube.cube_from_point(other.dimensions)) + # TODO: maybe two different methods are more readable and easier to debug def overlap(self, hypercubes: Iterable[HyperCube] | HyperCube) -> HyperCube | bool | None: if isinstance(hypercubes, Iterable): @@ -335,8 +346,8 @@ def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, norma class ClassificationCube(HyperCube): - def __init__(self, dimension: dict[str, tuple] = None): - super().__init__(dimension=dimension) + def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None, output: str = ""): + super().__init__(dimension=dimension, limits=limits, output=output) def update(self, dataset: pd.DataFrame, predictor) -> None: filtered = self._filter_dataframe(dataset.iloc[:, :-1]) @@ -346,7 +357,7 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: self._diversity = 1 - sum(prediction == self.output for prediction in predictions) / len(filtered) def copy(self) -> ClassificationCube: - return ClassificationCube(self.dimensions.copy()) + return ClassificationCube(self.dimensions.copy(), self._limits.copy(), self._output) class ClosedCube(HyperCube): From d97bec366dca55d3bb94235f7570dbb92fcefdf0 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 5 Jun 2023 02:56:36 +0200 Subject: [PATCH 35/67] feat(divine): added option for vicinity to corners --- demo/DemoClassification.ipynb | 80 +++++++++++++++++-- psyke/__init__.py | 5 +- .../extraction/hypercubic/divine/__init__.py | 31 +++++-- 3 files changed, 101 insertions(+), 15 deletions(-) diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 6d076555..8277026d 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -165,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 7, "id": "aa8a3128", "metadata": {}, "outputs": [ @@ -189,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 8, "outputs": [], "source": [ "def print_scores(scores):\n", @@ -220,7 +220,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 9, "outputs": [ { "name": "stdout", @@ -257,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 10, "outputs": [ { "name": "stdout", @@ -269,17 +269,55 @@ "\n", "DiViNE extracted rules:\n", "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " SepalLength in [5.2, 7.0], SepalWidth in [2.2, 3.2], PetalLength in [3.5, 4.9], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" + ] + } + ], + "source": [ + "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=True)\n", + "theory_from_divine = divine.extract(train)\n", + "scores, completeness = get_scores(divine, test, predictor)\n", + "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DiViNE performance (3 rules with 73.68% coverage):\n", + "Classification accuracy = 0.96 (data), 1.00 (BB)\n", + "F1 = 0.96 (data), 1.00 (BB)\n", + "\n", + "DiViNE extracted rules:\n", + "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n" + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" ] } ], "source": [ - "divine = Extractor.divine(predictor, k=5, patience=15)\n", + "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=False)\n", "theory_from_divine = divine.extract(train)\n", "scores, completeness = get_scores(divine, test, predictor)\n", "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", @@ -293,6 +331,32 @@ } } }, + { + "cell_type": "code", + "execution_count": 12, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'dfdgh' is not defined", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mNameError\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_21096/3072512347.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[1;32m----> 1\u001B[1;33m \u001B[0mdfdgh\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;31mNameError\u001B[0m: name 'dfdgh' is not defined" + ] + } + ], + "source": [ + "dfdgh" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": null, diff --git a/psyke/__init__.py b/psyke/__init__.py index e8d1564a..7f980a5f 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -219,13 +219,14 @@ def cart(predictor, max_depth: int = 3, max_leaves: int = 3, simplify=simplify) @staticmethod - def divine(predictor, k: int = 5, patience: int = 15, + def divine(predictor, k: int = 5, patience: int = 15, close_to_center: bool = True, discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: """ Creates a new DiViNE extractor. """ from psyke.extraction.hypercubic.divine import DiViNE - return DiViNE(predictor, k=k, patience=patience, discretization=discretization, normalization=normalization) + return DiViNE(predictor, k=k, patience=patience, close_to_center=close_to_center, + discretization=discretization, normalization=normalization) @staticmethod def iter(predictor, min_update: float = 0.1, n_points: int = 1, max_iterations: int = 600, min_examples: int = 250, diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index fe1735d9..ff023fcb 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -15,10 +15,12 @@ class DiViNE(HyperCubeExtractor): Explanator implementing DiViNE algorithm. """ - def __init__(self, predictor, k: int = 5, patience: int = 15, discretization=None, normalization=None): + def __init__(self, predictor, k: int = 5, patience: int = 15, close_to_center: bool = True, + discretization=None, normalization=None): super().__init__(predictor, Target.CLASSIFICATION, discretization, normalization) self.k = k self.patience = patience + self.vicinity_function = DiViNE.__closest_to_center if close_to_center else DiViNE.__closest_to_corners @staticmethod def __pop(data: pd.DataFrame, idx: int = None) -> (Point, pd.DataFrame): @@ -45,6 +47,24 @@ def __clean(self, data: pd.DataFrame) -> pd.DataFrame: # instances with neighbors of different classes are discarded return data[count == 1] + def __sort_cubes(self): + cubes = [(cube.diversity, i, cube) for i, cube in enumerate(self._hypercubes)] + cubes.sort() + self._hypercubes = [cube[2] for cube in cubes] + + def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFrame): + tree = BallTree(data.iloc[:, :-1], leaf_size=2) + return DiViNE.__pop(data,self.vicinity_function(tree, cube)) + + @staticmethod + def __closest_to_center(tree: BallTree, cube: GenericCube): + return tree.query([list(cube.center().dimensions.values())], k=1)[1][0][-1] + + @staticmethod + def __closest_to_corners(tree: BallTree, cube: GenericCube): + distance, idx = tree.query([list(point.dimensions.values()) for point in cube.corners()], k=1) + return idx[np.argmin(distance)][-1] + def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: data = self.__clean(dataframe) @@ -55,16 +75,17 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort cube = self.__to_cube(point) while patience > 0 and len(data) > 0: - tree = BallTree(data.iloc[:, :-1], leaf_size=2) - _, idx = tree.query([list(cube.center().dimensions.values())], k=1) - other, data = DiViNE.__pop(data, idx[0][-1]) + other, data = self.__closest(data, cube) if cube.output == list(other.dimensions.values())[-1]: cube = cube.merge_with_point(other) data = data[~(cube.filter_indices(data.iloc[:, :-1]))].reset_index(drop=True) else: patience -= 1 discarded.append(other) - self._hypercubes.append(cube) + if cube.volume() > 0: + cube.update(dataframe, self.predictor) + self._hypercubes.append(cube) if len(discarded) > 0: data = pd.concat([data] + [d.to_dataframe() for d in discarded]).reset_index(drop=True) + self.__sort_cubes() return self._create_theory(dataframe, sort) From 692321b4ada143cf8fc830d4c765d6bdfa3876e9 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 11 Jun 2023 03:28:04 +0200 Subject: [PATCH 36/67] feat(hypercube): density-based vicinity --- .gitignore | 3 +- demo/DemoClassification.ipynb | 165 ++++++++++++++---- psyke/__init__.py | 11 +- psyke/extraction/hypercubic/__init__.py | 49 +++++- .../extraction/hypercubic/divine/__init__.py | 5 +- psyke/extraction/hypercubic/hypercube.py | 39 ++++- psyke/extraction/hypercubic/iter/__init__.py | 1 - .../extraction/hypercubic/test_hypercube.py | 2 +- 8 files changed, 225 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index fc613289..cc76eb82 100644 --- a/.gitignore +++ b/.gitignore @@ -159,4 +159,5 @@ dmypy.json # Local stuff dummy/ tmp_model/ -plots/ \ No newline at end of file +plots/ +demo/ \ No newline at end of file diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 8277026d..80848efe 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -198,8 +198,8 @@ " f'F1 = {scores[EvaluableModel.ClassificationScore.F1][0]:.2f} (data), '\n", " f'{scores[EvaluableModel.ClassificationScore.F1][1]:.2f} (BB)')\n", "\n", - "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, EvaluableModel.Task.CLASSIFICATION,\n", + "def get_scores(extractor, test, predictor, brute=False, criterion='density', n=2):\n", + " return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n", " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" ], "metadata": { @@ -267,10 +267,14 @@ "Classification accuracy = 0.96 (data), 1.00 (BB)\n", "F1 = 0.96 (data), 1.00 (BB)\n", "\n", + "DiViNE brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", "DiViNE extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [5.2, 7.0], SepalWidth in [2.2, 3.2], PetalLength in [3.5, 4.9], PetalWidth in [1.0, 1.5].\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", @@ -284,6 +288,11 @@ "scores, completeness = get_scores(divine, test, predictor)\n", "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(divine, test, predictor, True, 'density')\n", + "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" ], "metadata": { @@ -295,20 +304,24 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 34, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "DiViNE performance (3 rules with 73.68% coverage):\n", + "DiViNE performance (3 rules with 71.05% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", "F1 = 0.96 (data), 1.00 (BB)\n", "\n", + "DiViNE brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", "DiViNE extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.7], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", @@ -322,6 +335,11 @@ "scores, completeness = get_scores(divine, test, predictor)\n", "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(divine, test, predictor, True)\n", + "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" ], "metadata": { @@ -333,40 +351,43 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 42, "outputs": [ { - "ename": "NameError", - "evalue": "name 'dfdgh' is not defined", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mNameError\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_21096/3072512347.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[1;32m----> 1\u001B[1;33m \u001B[0mdfdgh\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;31mNameError\u001B[0m: name 'dfdgh' is not defined" + "name": "stdout", + "output_type": "stream", + "text": [ + "ITER performance (3 rules with 97.37% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", + "ITER brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.97 (data), 1.00 (BB)\n", + "F1 = 0.97 (data), 1.00 (BB)\n", + "\n", + "ITER extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 2.58], PetalWidth in [0.09, 2.50].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [2.58, 4.94], PetalWidth in [0.09, 2.50].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [4.94, 6.90], PetalWidth in [0.09, 2.50].\n" ] } ], "source": [ - "dfdgh" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "it = Extractor.iter(predictor, min_update=0.15, min_examples=150, threshold=0.1, max_iterations=600, n_points=1)\n", + "it = Extractor.iter(predictor, min_update=0.2, min_examples=150, threshold=0.1,\n", + " max_iterations=100, n_points=1, fill_gaps=True)\n", "theory_from_iter = it.extract(train)\n", "scores, completeness = get_scores(it, test, predictor)\n", "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(it, test, predictor, True)\n", + "print(f'\\nITER brute performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" ], "metadata": { @@ -387,8 +408,83 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 40, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GridEx performance (5 rules with 94.74% coverage):\n", + "Classification accuracy = 0.94 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", + "\n", + "GridEx brute performance (5 rules with 100.00% coverage):\n", + "Classification accuracy = 0.95 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", + "\n", + "GridEx extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [4.54, 5.72], PetalWidth in [1.06, 1.54].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " PetalLength in [0.99, 2.17], PetalWidth in [0.09, 1.06].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [2.17, 4.54], PetalWidth in [0.57, 1.06].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [3.36, 4.54], PetalWidth in [1.06, 2.02].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " PetalLength in [4.54, 6.90], PetalWidth in [1.54, 2.50].\n" + ] + } + ], + "source": [ + "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", + "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.7, 5)])), threshold=.1, min_examples=1)\n", + "theory_from_gridEx = gridEx.extract(train)\n", + "scores, completeness = get_scores(gridEx, test, predictor)\n", + "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(gridEx, test, predictor, True)\n", + "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", + "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 41, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GridEx performance (3 rules with 94.74% coverage):\n", + "Classification accuracy = 0.92 (data), 0.94 (BB)\n", + "F1 = 0.92 (data), 0.95 (BB)\n", + "\n", + "GridEx brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.92 (data), 0.95 (BB)\n", + "F1 = 0.92 (data), 0.95 (BB)\n", + "\n", + "GridEx extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " PetalLength in [0.99, 2.47].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " PetalLength in [3.21, 4.68].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " PetalLength in [4.68, 6.90].\n" + ] + } + ], "source": [ "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", @@ -396,6 +492,11 @@ "scores, completeness = get_scores(gridEx, test, predictor)\n", "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", "print_scores(scores)\n", + "\n", + "scores, completeness = get_scores(gridEx, test, predictor, True)\n", + "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "\n", "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" ], "metadata": { diff --git a/psyke/__init__.py b/psyke/__init__.py index 7f980a5f..3e7ae087 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -62,9 +62,12 @@ def predict(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None) -> It ys = [inverse_mapping[y] for y in ys] return ys - def _predict(self, dataframe: pd.DataFrame) -> Iterable: + def _predict(self, dataframe: pd.DataFrame, criterion: str = 'perimeter') -> Iterable: raise NotImplementedError('predict') + def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + raise NotImplementedError('brute_predict') + def unscale(self, values, name): if self.normalization is None or isinstance(values, LinearRegression): return values @@ -76,9 +79,13 @@ def unscale(self, values, name): return values def score(self, dataframe: pd.DataFrame, predictor=None, fidelity: bool = False, completeness: bool = True, + brute: bool = False, criterion: str = 'corners', n: int = 2, task: EvaluableModel.Task = Task.CLASSIFICATION, scoring_function: Iterable[EvaluableModel.Score] = [ClassificationScore.ACCURACY]): - extracted = np.array(self.predict(dataframe.iloc[:, :-1])) + extracted = np.array( + self.predict(dataframe.iloc[:, :-1]) if not brute else + self.brute_predict(dataframe.iloc[:, :-1], criterion, n) + ) idx = [prediction is not None for prediction in extracted] y_extracted = extracted[idx] true = [dataframe.iloc[idx, -1]] diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index e0233eb7..fe235c1d 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -10,10 +10,12 @@ from tuprolog.core import Var, Struct, clause from tuprolog.theory import Theory, mutable_theory from psyke import logger, PedagogicalExtractor -from psyke.extraction.hypercubic.hypercube import HyperCube, RegressionCube, ClassificationCube, ClosedCube +from psyke.extraction.hypercubic.hypercube import HyperCube, RegressionCube, ClassificationCube, ClosedCube, Point, \ + GenericCube from psyke.utils.logic import create_variable_list, create_head, to_var, Simplifier from psyke.utils import Target, get_int_precision from psyke.extraction.hypercubic.strategy import Strategy, FixedStrategy +from sklearn.neighbors import BallTree class HyperCubePredictor: @@ -23,12 +25,49 @@ def __init__(self, output=Target.CONSTANT, normalization=None): self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: - return np.array([self._predict_from_cubes(dict(row.to_dict())) for _, row in dataframe.iterrows()]) + return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - def _predict_from_cubes(self, data: dict[str, float]) -> float | None: - data = {k: v for k, v in data.items()} + def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + predictions = self._predict(dataframe) + idx = [prediction is None for prediction in predictions] + + tree, mapping = self._create_brute_tree(dataframe, criterion, n) + + predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( + row.to_dict(), tree, mapping + ) for _, row in dataframe[idx].iterrows()]) + return predictions + + @staticmethod + def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, + mapping: dict[int, GenericCube]) -> float | str: + idx = tree.query([list(row.values())], k=1)[1][0][0] + return HyperCubePredictor._get_cube_output(mapping[idx], row) + + def _create_brute_tree(self, dataframe: pd.DataFrame, + criterion: str = 'center', n: int = 2) -> (BallTree, dict[int, GenericCube]): + points = None + if criterion == 'center': + points = [(cube.center(), i, cube) for i, cube in enumerate(self._hypercubes)] + elif criterion == 'density': + points = [(cube.filter_dataframe(dataframe).describe().loc['mean'], i, cube) + for i, cube in enumerate(self._hypercubes)] + points = [(Point(df.index.values, df.values), i, cube) for df, i, cube in points] + elif criterion == 'corner': + points = [(corner, cube) for cube in self._hypercubes for corner in cube.corners()] + points = [(point[0], i, point[1]) for i, point in enumerate(points)] + elif criterion == 'perimeter': + points = [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] + points = [(point[0], i, point[1]) for i, point in enumerate(points)] + else: + raise NotImplementedError("'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density']") + + return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True), leaf_size=2), \ + {point[1]: point[2] for point in points} + + def _predict_from_cubes(self, data: dict[str, float]) -> float | str | None: for cube in self._hypercubes: - if cube.__contains__(data): + if data in cube: if self._output == Target.CLASSIFICATION: return HyperCubePredictor._get_cube_output(cube, data) else: diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index ff023fcb..fff183bb 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -3,12 +3,11 @@ from tuprolog.theory import Theory from psyke import Target -from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube +from psyke.extraction.hypercubic import HyperCubeExtractor +from psyke.extraction.hypercubic.hypercube import Point, GenericCube, HyperCube from sklearn.neighbors import BallTree -from psyke.extraction.hypercubic.hypercube import Point, GenericCube - class DiViNE(HyperCubeExtractor): """ diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index f122d104..777c5e61 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -52,6 +52,9 @@ def dimensions(self) -> dict[str, float | str]: def to_dataframe(self) -> pd.DataFrame: return pd.DataFrame(data=[self.dimensions.values()], columns=list(self.dimensions.keys())) + def copy(self) -> Point: + return Point(list(self._dimensions.keys()), list(self._dimensions.values())) + class HyperCube: """ @@ -128,7 +131,7 @@ def filter_indices(self, dataset: pd.DataFrame) -> ndarray: ds = dataset.to_numpy(copy=True) return np.all((v[:, 0] <= ds) & (ds < v[:, 1]), axis=1) - def _filter_dataframe(self, dataset: pd.DataFrame) -> pd.DataFrame: + def filter_dataframe(self, dataset: pd.DataFrame) -> pd.DataFrame: return dataset[self.filter_indices(dataset)] def _zip_dimensions(self, other: HyperCube) -> list[ZippedDimension]: @@ -169,7 +172,7 @@ def copy(self) -> HyperCube: return HyperCube(self.dimensions.copy(), self._limits.copy(), self.output) def count(self, dataset: pd.DataFrame) -> int: - return self._filter_dataframe(dataset.iloc[:, :-1]).shape[0] + return self.filter_dataframe(dataset.iloc[:, :-1]).shape[0] def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, normalization=None) -> Iterable[Struct]: dimensions = dict(self.dimensions) @@ -260,6 +263,32 @@ def corners(self) -> Iterable[Point]: Point(list(self._dimensions.keys()), values) for values in itertools.product(*self._dimensions.values()) ] + def perimeter_samples(self, n: int = 5) -> Iterable[Point]: + def duplicate(point: Point, feature: str) -> Iterable[Point]: + new_point_a = point.copy() + new_point_b = point.copy() + new_point_a[feature] = self.get_first(feature) + new_point_b[feature] = self.get_second(feature) + return [new_point_a, new_point_b] + + def split(point: Point, feature: str, n: int): + points = [] + for value in np.linspace(self.get_first(feature), self.get_second(feature), n): + new_point = point.copy() + new_point[feature] = value + points.append(new_point) + return points + + points = [] + for primary in self._dimensions: + new_points = [Point([], [])] + for secondary in self._dimensions: + if primary != secondary: + new_points = np.array([duplicate(point, secondary) for point in new_points]).flatten() + new_points = np.array([split(point, primary, n) for point in new_points]).flatten() + points = points + list(new_points) + return points + def is_adjacent(self, cube: HyperCube) -> str | None: adjacent = None for (feature, [a1, b1]) in self._dimensions.items(): @@ -309,7 +338,7 @@ def update_dimension(self, feature: str, lower: float | tuple[float, float], upp self.update_dimension(feature, (lower, upper)) def update(self, dataset: pd.DataFrame, predictor) -> None: - filtered = self._filter_dataframe(dataset.iloc[:, :-1]) + filtered = self.filter_dataframe(dataset.iloc[:, :-1]) predictions = predictor.predict(filtered) self._output = np.mean(predictions) self._diversity = np.std(predictions) @@ -324,7 +353,7 @@ def __init__(self, dimension: dict[str, tuple] = None): super().__init__(dimension=dimension, output=LinearRegression()) def update(self, dataset: pd.DataFrame, predictor) -> None: - filtered = self._filter_dataframe(dataset.iloc[:, :-1]) + filtered = self.filter_dataframe(dataset.iloc[:, :-1]) if len(filtered > 0): predictions = predictor.predict(filtered) self._output.fit(filtered, predictions) @@ -350,7 +379,7 @@ def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None super().__init__(dimension=dimension, limits=limits, output=output) def update(self, dataset: pd.DataFrame, predictor) -> None: - filtered = self._filter_dataframe(dataset.iloc[:, :-1]) + filtered = self.filter_dataframe(dataset.iloc[:, :-1]) if len(filtered > 0): predictions = predictor.predict(filtered) self._output = mode(predictions) diff --git a/psyke/extraction/hypercubic/iter/__init__.py b/psyke/extraction/hypercubic/iter/__init__.py index 7f2ca76d..1e6d8237 100644 --- a/psyke/extraction/hypercubic/iter/__init__.py +++ b/psyke/extraction/hypercubic/iter/__init__.py @@ -5,7 +5,6 @@ import pandas as pd from sklearn.base import ClassifierMixin from tuprolog.theory import Theory -from psyke import PedagogicalExtractor from psyke.extraction.hypercubic import HyperCube, HyperCubeExtractor from psyke.extraction.hypercubic.hypercube import GenericCube from psyke.extraction.hypercubic.utils import MinUpdate, Expansion diff --git a/test/psyke/extraction/hypercubic/test_hypercube.py b/test/psyke/extraction/hypercubic/test_hypercube.py index d1ee273a..b241029c 100644 --- a/test/psyke/extraction/hypercubic/test_hypercube.py +++ b/test/psyke/extraction/hypercubic/test_hypercube.py @@ -162,7 +162,7 @@ def test_filter_dataframe(self): expected = (self.dataset.X >= self.x[0]) & (self.dataset.X < self.x[1]) & \ (self.dataset.Y >= self.y[0]) & (self.dataset.Y < self.y[1]) expected = self.dataset[expected].iloc[:, :-1] - filtered = self.cube._filter_dataframe(self.dataset.iloc[:, :-1]) + filtered = self.cube.filter_dataframe(self.dataset.iloc[:, :-1]) self.assertTrue(all(expected == filtered)) def test_update(self): From bcad37f16b202b0b754892d20fa2d0b7acbe5c14 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Fri, 16 Jun 2023 22:03:46 +0200 Subject: [PATCH 37/67] feat(divine): added vicinity to perimeter and barycenter --- .gitignore | 2 +- psyke/extraction/hypercubic/__init__.py | 23 +++---- .../extraction/hypercubic/divine/__init__.py | 6 +- psyke/extraction/hypercubic/hypercube.py | 16 ++++- psyke/utils/plot.py | 69 ++++++++++++++++++- requirements.txt | 2 +- 6 files changed, 93 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index cc76eb82..ca16d0a6 100644 --- a/.gitignore +++ b/.gitignore @@ -160,4 +160,4 @@ dmypy.json dummy/ tmp_model/ plots/ -demo/ \ No newline at end of file +demo/ diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index fe235c1d..ab29f5f2 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -31,39 +31,34 @@ def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: i predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] - tree, mapping = self._create_brute_tree(dataframe, criterion, n) + tree, cubes = self._create_brute_tree(criterion, n) predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( - row.to_dict(), tree, mapping + row.to_dict(), tree, cubes ) for _, row in dataframe[idx].iterrows()]) return predictions @staticmethod def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, - mapping: dict[int, GenericCube]) -> float | str: + cubes: list[GenericCube]) -> float | str: idx = tree.query([list(row.values())], k=1)[1][0][0] - return HyperCubePredictor._get_cube_output(mapping[idx], row) + return HyperCubePredictor._get_cube_output(cubes[idx], row) - def _create_brute_tree(self, dataframe: pd.DataFrame, - criterion: str = 'center', n: int = 2) -> (BallTree, dict[int, GenericCube]): + def _create_brute_tree(self, criterion: str = 'center', n: int = 2) -> (BallTree, list[GenericCube]): points = None if criterion == 'center': - points = [(cube.center(), i, cube) for i, cube in enumerate(self._hypercubes)] + points = [(cube.center(), cube) for cube in self._hypercubes] elif criterion == 'density': - points = [(cube.filter_dataframe(dataframe).describe().loc['mean'], i, cube) - for i, cube in enumerate(self._hypercubes)] - points = [(Point(df.index.values, df.values), i, cube) for df, i, cube in points] + points = [(cube.barycenter, cube) for cube in self._hypercubes] elif criterion == 'corner': points = [(corner, cube) for cube in self._hypercubes for corner in cube.corners()] - points = [(point[0], i, point[1]) for i, point in enumerate(points)] elif criterion == 'perimeter': points = [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] - points = [(point[0], i, point[1]) for i, point in enumerate(points)] else: raise NotImplementedError("'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density']") - return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True), leaf_size=2), \ - {point[1]: point[2] for point in points} + return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True)), \ + [point[1] for point in points] def _predict_from_cubes(self, data: dict[str, float]) -> float | str | None: for cube in self._hypercubes: diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index fff183bb..014f111b 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -39,8 +39,7 @@ def __to_cube(self, point: Point) -> GenericCube: return cube def __clean(self, data: pd.DataFrame) -> pd.DataFrame: - tree = BallTree(data.iloc[:, :-1], leaf_size=2) - _, idx = tree.query(data.iloc[:, :-1], k=self.k) + _, idx = BallTree(data.iloc[:, :-1]).query(data.iloc[:, :-1], k=self.k) # how many output classes are associated with the k neighbors count = np.array(list(map(lambda indices: len(data.iloc[indices].iloc[:, -1].unique()), idx))) # instances with neighbors of different classes are discarded @@ -52,8 +51,7 @@ def __sort_cubes(self): self._hypercubes = [cube[2] for cube in cubes] def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFrame): - tree = BallTree(data.iloc[:, :-1], leaf_size=2) - return DiViNE.__pop(data,self.vicinity_function(tree, cube)) + return DiViNE.__pop(data,self.vicinity_function(BallTree(data.iloc[:, :-1]), cube)) @staticmethod def __closest_to_center(tree: BallTree, cube: GenericCube): diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 777c5e61..e7b29479 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -70,6 +70,7 @@ def __init__(self, dimension: dict[str, tuple[float, float]] = None, limits: set self._limits = limits if limits is not None else set() self._output = output self._diversity = 0.0 + self._barycenter = Point([], []) def __contains__(self, point: dict[str, float]) -> bool: """ @@ -114,6 +115,10 @@ def output(self) -> float | str | LinearRegression: def diversity(self) -> float: return self._diversity + @property + def barycenter(self) -> Point: + return self._barycenter + def _fit_dimension(self, dimension: dict[str, tuple[float, float]]) -> dict[str, tuple[float, float]]: new_dimension: dict[str, tuple[float, float]] = {} for key, value in dimension.items(): @@ -283,9 +288,8 @@ def split(point: Point, feature: str, n: int): for primary in self._dimensions: new_points = [Point([], [])] for secondary in self._dimensions: - if primary != secondary: - new_points = np.array([duplicate(point, secondary) for point in new_points]).flatten() - new_points = np.array([split(point, primary, n) for point in new_points]).flatten() + new_points = np.array([duplicate(point, secondary) if primary != secondary else + split(point, primary, n) for point in new_points]).flatten() points = points + list(new_points) return points @@ -342,6 +346,8 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: predictions = predictor.predict(filtered) self._output = np.mean(predictions) self._diversity = np.std(predictions) + means = filtered.describe().loc['mean'] + self._barycenter = Point(means.index.values, means.values) # TODO: why this is not a property? def init_diversity(self, std: float) -> None: @@ -358,6 +364,8 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: predictions = predictor.predict(filtered) self._output.fit(filtered, predictions) self._diversity = (abs(self._output.predict(filtered) - predictions)).mean() + means = filtered.describe().loc['mean'] + self._barycenter = Point(means.index.values, means.values) def copy(self) -> RegressionCube: return RegressionCube(self.dimensions.copy()) @@ -384,6 +392,8 @@ def update(self, dataset: pd.DataFrame, predictor) -> None: predictions = predictor.predict(filtered) self._output = mode(predictions) self._diversity = 1 - sum(prediction == self.output for prediction in predictions) / len(filtered) + means = filtered.describe().loc['mean'] + self._barycenter = Point(means.index.values, means.values) def copy(self) -> ClassificationCube: return ClassificationCube(self.dimensions.copy(), self._limits.copy(), self._output) diff --git a/psyke/utils/plot.py b/psyke/utils/plot.py index 2bcbca82..5db7a9e8 100644 --- a/psyke/utils/plot.py +++ b/psyke/utils/plot.py @@ -7,10 +7,75 @@ from matplotlib.lines import Line2D from tuprolog.solve.prolog import prolog_solver from tuprolog.theory import Theory, mutable_theory -from psyke.utils.logic import data_to_struct, pretty_theory, get_in_rule, get_not_in_rule + +from psyke.extraction.hypercubic import HyperCubeExtractor +from psyke.utils.logic import data_to_struct, get_in_rule, get_not_in_rule import matplotlib -matplotlib.use('TkAgg') +#matplotlib.use('TkAgg') + + +def plot_init(xlim, ylim, xlabel, ylabel, size=(4, 3), equal=False): + plt.figure(figsize=size) + if equal: + plt.gca().set_aspect(1) + plt.xlim(xlim) + plt.ylim(ylim) + plt.gca().set_xlabel(xlabel) + plt.gca().set_ylabel(ylabel) + plt.gca().set_rasterized(True) + + +def plot_point(x, y, color, marker): + plt.scatter(x, y, c=color, marker=marker) + + +def plot_classification_samples(dataframe, classes, colors, markers, labels, loc, name, show=True): + marks = [Line2D([0], [0], color=c, marker=m, lw="0") for c, m in zip(colors, markers)] + + for cl, c, m in zip(classes, colors, markers): + df = dataframe[dataframe.target == cl] + plot_point(df["petal length"], df["petal width"], c, m) + + plt.gca().legend(marks, labels, loc=loc) + plt.savefig("plot/{}.pdf".format(name), dpi=500, bbox_inches='tight') + if show: + plt.show() + + +def plot_boundaries(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + a: float = .5, h: str = '////////', ls='-', e=.05): + for cube in extractor._hypercubes: + plt.gca().fill_between((cube[x][0] - e, cube[x][1] + e), cube[y][0] - e, cube[y][1] + e, + fc='none', ec=colors[cube.output], alpha=a, hatch=h, linestyle=ls) + + +def plot_perimeters(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], n: int = 5, + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + for corner in cube.perimeter_samples(n): + plt.scatter(corner[x], corner[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) + + +def plot_centers(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + center = cube.center() + plt.scatter(center[x], center[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) + + +def plot_corners(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + for corner in cube.corners(): + plt.scatter(corner[x], corner[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) + + +def plot_barycenters(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], + ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): + for cube in extractor._hypercubes: + center = cube.barycenter + plt.scatter(center[x], center[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) def predict_from_theory(theory: Theory, data: pd.DataFrame) -> list[float or str]: diff --git a/requirements.txt b/requirements.txt index 0ef3f994..91d49f9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,4 @@ parameterized==0.9.0 protobuf==4.23.4 setuptools==68.0.0 kneed==0.8.5 -sympy==1.12 \ No newline at end of file +sympy==1.12 From 3d8a02c65a2787ea42dd42c4bdc7ac6db7706dcb Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sat, 17 Jun 2023 13:16:09 +0200 Subject: [PATCH 38/67] feat: added COSMiK extractor --- demo/DemoRegression.ipynb | 185 +++++++++++++++++- psyke/__init__.py | 63 ++++-- psyke/extraction/hypercubic/__init__.py | 5 + .../extraction/hypercubic/cosmik/__init__.py | 43 ++++ .../extraction/hypercubic/divine/__init__.py | 15 +- psyke/tuning/orchid/__init__.py | 3 +- psyke/tuning/pedro/__init__.py | 6 +- 7 files changed, 285 insertions(+), 35 deletions(-) create mode 100644 psyke/extraction/hypercubic/cosmik/__init__.py diff --git a/demo/DemoRegression.ipynb b/demo/DemoRegression.ipynb index 68f73ceb..1aa736c0 100644 --- a/demo/DemoRegression.ipynb +++ b/demo/DemoRegression.ipynb @@ -49,7 +49,7 @@ "outputs": [], "source": [ "dataset = pd.read_csv(\"../test/resources/datasets/df.csv\")\n", - "dataset = dataset[[\"X\", \"Y\", \"Z4\"]].dropna()\n", + "dataset = dataset[[\"X\", \"Y\", \"Z0\"]].dropna()\n", "#dataset = pd.read_csv(\"../test/resources/datasets/CCPP.csv\", sep=\";\", decimal=\",\")\n", "#dataset" ] @@ -69,7 +69,7 @@ "metadata": {}, "outputs": [], "source": [ - "train, test = train_test_split(dataset, test_size=0.5, random_state=10)" + "train, test = train_test_split(dataset, test_size=0.85, random_state=10)" ] }, { @@ -109,8 +109,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "MAE = 0.08\n", - "MSE = 0.03\n", + "MAE = 0.00\n", + "MSE = 0.00\n", "R2 = 1.00\n" ] } @@ -160,8 +160,8 @@ " f'{scores[EvaluableModel.RegressionScore.R2][1]:.2f} (BB)')\n", "\n", "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, EvaluableModel.Task.REGRESSION,\n", - " [EvaluableModel.RegressionScore.MAE, EvaluableModel.RegressionScore.MSE,\n", + " return extractor.score(test, predictor, True, True, task=EvaluableModel.Task.REGRESSION,\n", + " scoring_function=[EvaluableModel.RegressionScore.MAE, EvaluableModel.RegressionScore.MSE,\n", " EvaluableModel.RegressionScore.R2])" ] }, @@ -173,6 +173,179 @@ "We create several extractors that use ITER, GridEx and GridREx algorithms to extract prolog rules from the predictor." ] }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "COSMiK performance (3 rules with 90.03% coverage):\n", + "MAE = 0.11 (data), 0.11 (BB)\n", + "MSE = 0.05 (data), 0.05 (BB)\n", + "R2 = 0.92 (data), 0.92 (BB)\n", + "\n", + "COSMiK extracted rules:\n", + "\n", + "'Z0'(X, Y, 1.0) :-\n", + " X in [0.0, 0.48], Y in [0.0, 0.48].\n", + "'Z0'(X, Y, -0.98) :-\n", + " X in [0.65, 1.0], Y in [0.0, 0.46].\n", + "'Z0'(X, Y, -0.13) :-\n", + " X in [0.05, 0.99], Y in [0.59, 1.0].\n" + ] + } + ], + "source": [ + "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True)\n", + "theory_from_cosmik = cosmik.extract(train)\n", + "scores, completeness = get_scores(cosmik, test, predictor)\n", + "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "COSMiK performance (4 rules with 89.92% coverage):\n", + "MAE = 0.08 (data), 0.08 (BB)\n", + "MSE = 0.03 (data), 0.03 (BB)\n", + "R2 = 0.96 (data), 0.96 (BB)\n", + "\n", + "COSMiK extracted rules:\n", + "\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.0, 0.48], Y in [0.0, 0.48], Z0 is 1.0.\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.65, 1.0], Y in [0.0, 0.44], Z0 is -1.0.\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.94, 1.0], Y in [0.79, 1.0], Z0 is -1.0.\n", + "'Z0'(X, Y, Z0) :-\n", + " X in [0.05, 0.98], Y in [0.57, 0.99], Z0 is 0.53 - 0.82 * X - 0.28 * Y.\n" + ] + } + ], + "source": [ + "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True, output=Target.REGRESSION)\n", + "theory_from_cosmik = cosmik.extract(train)\n", + "scores, completeness = get_scores(cosmik, test, predictor)\n", + "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", + "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", + "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", + "\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", + "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", + "\n", + "**********************\n", + "Best Algorithm.GRIDEX\n", + "**********************\n", + "MAE = 2.75, 5 rules\n", + "Threshold = 0.00\n", + "Iterations = 1\n", + "Strategy = Adaptive ([(0.99, 5)])\n", + "\n", + "**********************\n", + "Best Predictive loss\n", + "**********************\n", + "MAE = 2.23, 9 rules\n", + "Threshold = 0.00\n", + "Iterations = 1\n", + "Strategy = Fixed (3)\n", + "\n", + "**********************\n", + "Best N rules\n", + "**********************\n", + "MAE = 4.06, 2 rules\n", + "Threshold = 0.00\n", + "Iterations = 1\n", + "Strategy = Adaptive ([(0.99, 2)])\n", + "\n", + "GridEx performance (5 rules with 100.00% coverage):\n", + "MAE = 2.79 (data), 2.79 (BB)\n", + "MSE = 14.54 (data), 14.52 (BB)\n", + "R2 = 0.42 (data), 0.42 (BB)\n", + "\n", + "GridEx extracted rules:\n", + "\n", + "'Z4'(X, Y, 3.99) :-\n", + " Y in [-0.00, 0.19].\n", + "'Z4'(X, Y, 6.53) :-\n", + " Y in [0.19, 0.4].\n", + "'Z4'(X, Y, 9.37) :-\n", + " Y in [0.4, 0.6].\n", + "'Z4'(X, Y, 11.42) :-\n", + " Y in [0.6, 0.80].\n", + "'Z4'(X, Y, 2.75) :-\n", + " Y in [0.80, 1.00].\n" + ] + } + ], + "source": [ + "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", + " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL)\n", + "pedro.search()\n", + "(_, _, threshold, grid) = pedro.get_best()[0]\n", + "\n", + "gridEx = Extractor.gridex(predictor, grid, threshold=threshold)\n", + "theory_from_gridEx = gridEx.extract(train)\n", + "scores, completeness = get_scores(gridEx, test, predictor)\n", + "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", + "print_scores(scores)\n", + "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": 8, diff --git a/psyke/__init__.py b/psyke/__init__.py index 3e7ae087..5821d368 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -160,59 +160,81 @@ def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: """ raise NotImplementedError('extract') - def mae(self, dataframe: pd.DataFrame, predictor=None) -> float: + def mae(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' MAE w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the mean absolute error. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the mean absolute error (MAE) of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.REGRESSION, - [Extractor.RegressionScore.MAE])[Extractor.RegressionScore.MAE][-1] + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.REGRESSION, [Extractor.RegressionScore.MAE])[Extractor.RegressionScore.MAE][-1] - def mse(self, dataframe: pd.DataFrame, predictor=None) -> float: + def mse(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' MSE w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the mean squared error. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the mean squared error (MSE) of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.REGRESSION, - [Extractor.RegressionScore.MSE])[Extractor.RegressionScore.MSE][-1] + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.REGRESSION, [Extractor.RegressionScore.MSE])[Extractor.RegressionScore.MSE][-1] - def r2(self, dataframe: pd.DataFrame, predictor=None) -> float: + def r2(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' R2 score w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the R2 score. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the R2 score of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, Extractor.Task.REGRESSION, [Extractor.RegressionScore.R2])[Extractor.RegressionScore.R2][-1] - def accuracy(self, dataframe: pd.DataFrame, predictor=None) -> float: + def accuracy(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' accuracy classification score w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the accuracy classification score. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the accuracy classification score of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.CLASSIFICATION, + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.CLASSIFICATION, [Extractor.ClassificationScore.ACCURACY])[Extractor.ClassificationScore.ACCURACY][-1] - def f1(self, dataframe: pd.DataFrame, predictor=None) -> float: + def f1(self, dataframe: pd.DataFrame, predictor=None, brute: bool = False, criterion: str = 'center', + n: int = 3) -> float: """ Calculates the predictions' F1 score w.r.t. the instances given as input. :param dataframe: is the set of instances to be used to calculate the F1 score. :param predictor: if provided, its predictions on the dataframe are taken instead of the dataframe instances. + :param brute: if True, a brute prediction is executed. + :param criterion: creterion for brute prediction. + :param n: number of points for brute prediction with 'perimeter' criterion. :return: the F1 score of the predictions. """ - return self.score(dataframe, predictor, predictor is not None, False, Extractor.Task.CLASSIFICATION, + return self.score(dataframe, predictor, predictor is not None, False, brute, criterion, n, + Extractor.Task.CLASSIFICATION, [Extractor.ClassificationScore.F1])[Extractor.ClassificationScore.F1][-1] @staticmethod @@ -227,7 +249,7 @@ def cart(predictor, max_depth: int = 3, max_leaves: int = 3, @staticmethod def divine(predictor, k: int = 5, patience: int = 15, close_to_center: bool = True, - discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: + discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: """ Creates a new DiViNE extractor. """ @@ -235,6 +257,17 @@ def divine(predictor, k: int = 5, patience: int = 15, close_to_center: bool = Tr return DiViNE(predictor, k=k, patience=patience, close_to_center=close_to_center, discretization=discretization, normalization=normalization) + @staticmethod + def cosmik(predictor, max_components: int = 4, k: int = 5, patience: int = 15, close_to_center: bool = True, + output: Target = Target.CONSTANT, + discretization: Iterable[DiscreteFeature] = None, normalization=None) -> Extractor: + """ + Creates a new COSMiK extractor. + """ + from psyke.extraction.hypercubic.cosmik import COSMiK + return COSMiK(predictor, max_components=max_components, k=k, patience=patience, close_to_center=close_to_center, + output=output, discretization=discretization, normalization=normalization) + @staticmethod def iter(predictor, min_update: float = 0.1, n_points: int = 1, max_iterations: int = 600, min_examples: int = 250, threshold: float = 0.1, fill_gaps: bool = True, normalization: dict[str, tuple[float, float]] = None, @@ -267,8 +300,8 @@ def gridrex(predictor, grid, min_examples: int = 250, threshold: float = 0.1, return GridREx(predictor, grid, min_examples, threshold, normalization, seed) @staticmethod - def creepy(predictor, clustering, depth: int, error_threshold: float, output, gauss_components: int = 2, - ranks: [(str, float)] = [], ignore_threshold: float = 0.0, + def creepy(predictor, clustering, depth: int, error_threshold: float, output: Target = Target.CONSTANT, + gauss_components: int = 2, ranks: [(str, float)] = [], ignore_threshold: float = 0.0, normalization: dict[str, tuple[float, float]] = None) -> Extractor: """ Creates a new CReEPy extractor. diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index ab29f5f2..0bb73946 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -95,6 +95,11 @@ def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: return RegressionCube() return ClassificationCube() + def _sort_cubes(self): + cubes = [(cube.diversity, i, cube) for i, cube in enumerate(self._hypercubes)] + cubes.sort() + self._hypercubes = [cube[2] for cube in cubes] + @staticmethod def _create_head(dataframe: pd.DataFrame, variables: list[Var], output: float | LinearRegression) -> Struct: return create_head(dataframe.columns[-1], variables[:-1], output) \ diff --git a/psyke/extraction/hypercubic/cosmik/__init__.py b/psyke/extraction/hypercubic/cosmik/__init__.py new file mode 100644 index 00000000..7412a96d --- /dev/null +++ b/psyke/extraction/hypercubic/cosmik/__init__.py @@ -0,0 +1,43 @@ +import pandas as pd +from sklearn.mixture import GaussianMixture +from tuprolog.theory import Theory + +from psyke import Target, Extractor +from psyke.clustering.utils import select_gaussian_mixture +from psyke.extraction.hypercubic import HyperCube, HyperCubeExtractor, RegressionCube + + +class COSMiK(HyperCubeExtractor): + """ + Explanator implementing COSMiK algorithm. + """ + + def __init__(self, predictor, max_components: int = 4, k: int = 5, patience: int = 15, close_to_center: bool = True, + output: Target = Target.CONSTANT, discretization=None, normalization=None): + super().__init__(predictor, Target.REGRESSION, discretization, normalization) + self.max = max_components + self.k = k + self.patience = patience + self.output = output + self.close_to_center = close_to_center + + def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: + X, y = dataframe.iloc[:, :-1], dataframe.iloc[:, -1] + + _, n, _ = select_gaussian_mixture(dataframe, self.max) + gmm = GaussianMixture(n) + gmm.fit(X, y) + + divine = Extractor.divine(gmm, self.k, self.patience, self.close_to_center, + self.discretization, self.normalization) + df = X.join(pd.DataFrame(gmm.predict(X))) + df.columns = dataframe.columns + divine.extract(df) + + self._hypercubes = [HyperCube(cube.dimensions) if self.output == Target.CONSTANT else + RegressionCube(cube.dimensions) for cube in divine._hypercubes] + for cube in self._hypercubes: + cube.update(dataframe, self.predictor) + + self._sort_cubes() + return self._create_theory(dataframe, sort) \ No newline at end of file diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index 014f111b..d5674776 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -19,7 +19,7 @@ def __init__(self, predictor, k: int = 5, patience: int = 15, close_to_center: b super().__init__(predictor, Target.CLASSIFICATION, discretization, normalization) self.k = k self.patience = patience - self.vicinity_function = DiViNE.__closest_to_center if close_to_center else DiViNE.__closest_to_corners + self.vicinity_function = DiViNE.closest_to_center if close_to_center else DiViNE.closest_to_corners @staticmethod def __pop(data: pd.DataFrame, idx: int = None) -> (Point, pd.DataFrame): @@ -45,20 +45,15 @@ def __clean(self, data: pd.DataFrame) -> pd.DataFrame: # instances with neighbors of different classes are discarded return data[count == 1] - def __sort_cubes(self): - cubes = [(cube.diversity, i, cube) for i, cube in enumerate(self._hypercubes)] - cubes.sort() - self._hypercubes = [cube[2] for cube in cubes] - def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFrame): - return DiViNE.__pop(data,self.vicinity_function(BallTree(data.iloc[:, :-1]), cube)) + return DiViNE.__pop(data, self.vicinity_function(BallTree(data.iloc[:, :-1]), cube)) @staticmethod - def __closest_to_center(tree: BallTree, cube: GenericCube): + def closest_to_center(tree: BallTree, cube: GenericCube): return tree.query([list(cube.center().dimensions.values())], k=1)[1][0][-1] @staticmethod - def __closest_to_corners(tree: BallTree, cube: GenericCube): + def closest_to_corners(tree: BallTree, cube: GenericCube): distance, idx = tree.query([list(point.dimensions.values()) for point in cube.corners()], k=1) return idx[np.argmin(distance)][-1] @@ -84,5 +79,5 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort self._hypercubes.append(cube) if len(discarded) > 0: data = pd.concat([data] + [d.to_dataframe() for d in discarded]).reset_index(drop=True) - self.__sort_cubes() + self._sort_cubes() return self._create_theory(dataframe, sort) diff --git a/psyke/tuning/orchid/__init__.py b/psyke/tuning/orchid/__init__.py index d1dfe6c8..cf76eefa 100644 --- a/psyke/tuning/orchid/__init__.py +++ b/psyke/tuning/orchid/__init__.py @@ -57,7 +57,8 @@ def __search_threshold(self, depth): (EvaluableModel.Task.CLASSIFICATION, EvaluableModel.ClassificationScore.INVERSE_ACCURACY) \ if self.output == Target.CLASSIFICATION else \ (EvaluableModel.Task.REGRESSION, EvaluableModel.RegressionScore.MAE) - p, n = clustering.score(self.dataframe, None, False, False, task, [metric])[metric][0], clustering.n_rules + p, n = clustering.score(self.dataframe, None, False, False, task=task, + scoring_function=[metric])[metric][0], clustering.n_rules print(f"Predictive loss = {p:.2f}, {n} rules") diff --git a/psyke/tuning/pedro/__init__.py b/psyke/tuning/pedro/__init__.py index feb57e5e..9e6aae3b 100644 --- a/psyke/tuning/pedro/__init__.py +++ b/psyke/tuning/pedro/__init__.py @@ -4,10 +4,10 @@ from psyke import Extractor from psyke.extraction.hypercubic import Grid, FeatureRanker from psyke.extraction.hypercubic.strategy import AdaptiveStrategy, FixedStrategy -from psyke.tuning import Objective, Optimizer +from psyke.tuning import Objective, GridOptimizer -class PEDRO(Optimizer): +class PEDRO(GridOptimizer): class Algorithm(Enum): GRIDEX = 1, GRIDREX = 2 @@ -71,7 +71,7 @@ def __search_depth(self, strategy, critical, max_partitions): for iterations in range(self.max_depth): grid = Grid(iterations + 1, strategy) p = self.__search_threshold(grid, critical, max_partitions) - b = Optimizer._best(p)[1] + b = GridOptimizer._best(p)[1] print() improvement = self._depth_improvement( [best[0], best[1]], [b[0], b[1]] From d2c16288e52ef2568569851ba81566f725596064 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:22:25 +0200 Subject: [PATCH 39/67] fix: normalization --- demo/DemoClassification.ipynb | 37 +++++++++++++++---------- demo/DemoRegressionScaled.ipynb | 36 +++++++++--------------- psyke/extraction/hypercubic/__init__.py | 9 ++++-- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 80848efe..3bd95396 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -265,20 +265,29 @@ "text": [ "DiViNE performance (3 rules with 73.68% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "\n", - "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "DiViNE extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" + "F1 = 0.96 (data), 1.00 (BB)\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/1973727682.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 7\u001B[1;33m \u001B[0mscores\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcompleteness\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdivine\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'density'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 8\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34mf'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 9\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/301734117.py\u001B[0m in \u001B[0;36mget_scores\u001B[1;34m(extractor, test, predictor, brute, criterion, n)\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mextractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbrute\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mFalse\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;34m'density'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 8\u001B[1;33m return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n\u001B[0m\u001B[0;32m 9\u001B[0m [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mscore\u001B[1;34m(self, dataframe, predictor, fidelity, completeness, brute, criterion, n, task, scoring_function)\u001B[0m\n\u001B[0;32m 85\u001B[0m extracted = np.array(\n\u001B[0;32m 86\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[0mbrute\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 87\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 88\u001B[0m )\n\u001B[0;32m 89\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mextracted\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " ] } ], diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index b57fab34..98331ff5 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -170,32 +170,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "ITER performance (9 rules):\n", - "MAE = 0.04\n", - "MAE fidelity = 0.04\n", - "R2 = 0.88\n", - "R2 fidelity = 0.88\n", + "ITER performance (4 rules):\n", + "MAE = 0.55\n", + "MAE fidelity = 0.55\n", + "R2 = -6.34\n", + "R2 fidelity = -6.34\n", "\n", "ITER extracted rules:\n", "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.46], Y in [0.00, 0.43].\n", - "z(X, Y, 0.59) :-\n", - " X in [0.00, 0.46], Y in [0.43, 0.53].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.46], Y in [0.53, 0.99].\n", - "z(X, Y, 0.47) :-\n", - " X in [0.46, 0.56], Y in [0.00, 0.43].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.56, 0.99], Y in [0.00, 0.43].\n", - "z(X, Y, 0.18) :-\n", - " X in [0.46, 0.56], Y in [0.53, 0.99].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.56, 0.99], Y in [0.53, 0.99].\n", - "z(X, Y, 0.32) :-\n", - " X in [0.46, 0.56], Y in [0.43, 0.53].\n", - "z(X, Y, 0.20) :-\n", - " X in [0.56, 0.99], Y in [0.43, 0.53].\n" + "z(X, Y, 1.11) :-\n", + " X in [-1.70, -0.00], Y in [-1.69, 0.22].\n", + "z(X, Y, 0.19) :-\n", + " X in [-1.70, -0.00], Y in [0.22, 1.75].\n", + "z(X, Y, -0.11) :-\n", + " X in [-0.00, 1.75], Y in [-1.69, 0.22].\n", + "z(X, Y, -1.29) :-\n", + " X in [-0.00, 1.75], Y in [0.22, 1.75].\n" ] } ], diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 0bb73946..e0fc7e95 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -25,7 +25,11 @@ def __init__(self, output=Target.CONSTANT, normalization=None): self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: - return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) + predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) + m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] + idx = [prediction is not None for prediction in predictions] + predictions[idx] = predictions[idx] * s + m + return predictions def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) @@ -36,7 +40,8 @@ def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: i predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( row.to_dict(), tree, cubes ) for _, row in dataframe[idx].iterrows()]) - return predictions + m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] + return np.array(predictions) * s + m @staticmethod def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, From 2b724643ea2e4ac1dd75fe5483d8d73103d8750f Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:34:45 +0200 Subject: [PATCH 40/67] fix(HyperCubeExtractor): remove cubes with count <= 1 --- psyke/__init__.py | 4 ++-- psyke/extraction/hypercubic/__init__.py | 4 ++++ psyke/extraction/hypercubic/cosmik/__init__.py | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index 5821d368..699eacaa 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -302,13 +302,13 @@ def gridrex(predictor, grid, min_examples: int = 250, threshold: float = 0.1, @staticmethod def creepy(predictor, clustering, depth: int, error_threshold: float, output: Target = Target.CONSTANT, gauss_components: int = 2, ranks: [(str, float)] = [], ignore_threshold: float = 0.0, - normalization: dict[str, tuple[float, float]] = None) -> Extractor: + discretization=None, normalization: dict[str, tuple[float, float]] = None) -> Extractor: """ Creates a new CReEPy extractor. """ from psyke.extraction.hypercubic.creepy import CReEPy return CReEPy(predictor, depth, error_threshold, output, gauss_components, ranks, ignore_threshold, - normalization, clustering) + discretization, normalization, clustering) @staticmethod def real(predictor, discretization=None) -> Extractor: diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index e0fc7e95..db3b9d1a 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -114,7 +114,11 @@ def _create_head(dataframe: pd.DataFrame, variables: list[Var], output: float | def _ignore_dimensions(self) -> Iterable[str]: return [] + def __drop(self, dataframe: pd.DataFrame): + self._hypercubes = [cube for cube in self._hypercubes if cube.count(dataframe) > 1] + def _create_theory(self, dataframe: pd.DataFrame, sort: bool = True) -> Theory: + self.__drop() new_theory = mutable_theory() for cube in self._hypercubes: logger.info(cube.output) diff --git a/psyke/extraction/hypercubic/cosmik/__init__.py b/psyke/extraction/hypercubic/cosmik/__init__.py index 7412a96d..c7be3a89 100644 --- a/psyke/extraction/hypercubic/cosmik/__init__.py +++ b/psyke/extraction/hypercubic/cosmik/__init__.py @@ -34,8 +34,8 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort df.columns = dataframe.columns divine.extract(df) - self._hypercubes = [HyperCube(cube.dimensions) if self.output == Target.CONSTANT else - RegressionCube(cube.dimensions) for cube in divine._hypercubes] + self._hypercubes = [HyperCube(cube.dimensions.copy()) if self.output == Target.CONSTANT else + RegressionCube(cube.dimensions.copy()) for cube in divine._hypercubes] for cube in self._hypercubes: cube.update(dataframe, self.predictor) From aa8cc93758a1e0ef84b64da4897c26a539c8f16a Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:01:51 +0200 Subject: [PATCH 41/67] fix(HyperCubeExtractor): fix cube removal --- psyke/extraction/hypercubic/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index db3b9d1a..ee2e293f 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -27,9 +27,7 @@ def __init__(self, output=Target.CONSTANT, normalization=None): def _predict(self, dataframe: pd.DataFrame) -> Iterable: predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] - idx = [prediction is not None for prediction in predictions] - predictions[idx] = predictions[idx] * s + m - return predictions + return np.array([None if prediction is None else prediction * s + m for prediction in predictions]) def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) @@ -118,7 +116,7 @@ def __drop(self, dataframe: pd.DataFrame): self._hypercubes = [cube for cube in self._hypercubes if cube.count(dataframe) > 1] def _create_theory(self, dataframe: pd.DataFrame, sort: bool = True) -> Theory: - self.__drop() + self.__drop(dataframe) new_theory = mutable_theory() for cube in self._hypercubes: logger.info(cube.output) From 7fd0c5016ec759f760a5ec8d20b68ae9fd1443e5 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:26:35 +0200 Subject: [PATCH 42/67] fix(HyperCubePredictor): scaled predictions --- psyke/extraction/hypercubic/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index ee2e293f..7e2abf8b 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -26,20 +26,20 @@ def __init__(self, output=Target.CONSTANT, normalization=None): def _predict(self, dataframe: pd.DataFrame) -> Iterable: predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] + m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] return np.array([None if prediction is None else prediction * s + m for prediction in predictions]) def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] - - tree, cubes = self._create_brute_tree(criterion, n) - - predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( - row.to_dict(), tree, cubes - ) for _, row in dataframe[idx].iterrows()]) - m, s = 0, 1 if self.normalization is None else self.normalization[dataframe.columns[-1]] - return np.array(predictions) * s + m + if sum(idx) > 0: + tree, cubes = self._create_brute_tree(criterion, n) + m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] + predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( + row.to_dict(), tree, cubes + ) * s + m for _, row in dataframe[idx].iterrows()]) + + return np.array(predictions) @staticmethod def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, From 3d6fa7db7935623b103cac1f3f0ba42938fcc294 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:32:23 +0200 Subject: [PATCH 43/67] fix: scaled predictions --- psyke/__init__.py | 17 +++++++++++++---- psyke/extraction/cart/__init__.py | 4 ++-- psyke/extraction/hypercubic/__init__.py | 10 +++------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index 699eacaa..9ed1e4ae 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -56,16 +56,25 @@ def predict(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None) -> It :param mapping: for one-hot encoding. :return: a list of predictions. """ - ys = self._predict(dataframe) + return self.__convert(self._predict(dataframe), mapping) + + def _predict(self, dataframe: pd.DataFrame) -> Iterable: + raise NotImplementedError('predict') + + def __convert(self, ys: Iterable, mapping: dict[str: int] = None) -> Iterable: if mapping is not None: inverse_mapping = {v: k for k, v in mapping.items()} ys = [inverse_mapping[y] for y in ys] + if self.normalization is not None: + m, s = self.normalization[list(self.normalization.keys())[-1]] + ys = [prediction if prediction is None else ys * s + m for prediction in ys] return ys - def _predict(self, dataframe: pd.DataFrame, criterion: str = 'perimeter') -> Iterable: - raise NotImplementedError('predict') + def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, + mapping: dict[str: int] = None) -> Iterable: + return self.__convert(self._brute_predict(dataframe), mapping) - def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: raise NotImplementedError('brute_predict') def unscale(self, values, name): diff --git a/psyke/extraction/cart/__init__.py b/psyke/extraction/cart/__init__.py index c2ceabff..444045ed 100644 --- a/psyke/extraction/cart/__init__.py +++ b/psyke/extraction/cart/__init__.py @@ -77,8 +77,8 @@ def _extract(self, data: pd.DataFrame, mapping: dict[str: int] = None, sort: boo self._cart_predictor.predictor.fit(data.iloc[:, :-1], data.iloc[:, -1]) return self._create_theory(data, mapping, sort) - def _predict(self, data) -> Iterable: - return self._cart_predictor.predict(data) + def _predict(self, dataframe: pd.DataFrame) -> Iterable: + return self._cart_predictor.predict(dataframe) @property def n_rules(self) -> int: diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 7e2abf8b..0db5a8d4 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -25,20 +25,16 @@ def __init__(self, output=Target.CONSTANT, normalization=None): self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: - predictions = np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] - return np.array([None if prediction is None else prediction * s + m for prediction in predictions]) + return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] if sum(idx) > 0: tree, cubes = self._create_brute_tree(criterion, n) - m, s = (0, 1) if self.normalization is None else self.normalization[list(self.normalization.keys())[-1]] predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( row.to_dict(), tree, cubes - ) * s + m for _, row in dataframe[idx].iterrows()]) - + ) for _, row in dataframe[idx].iterrows()]) return np.array(predictions) @staticmethod From 89eeed68c6ce59138d2ad8d5347ae080006e5293 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:42:07 +0200 Subject: [PATCH 44/67] fix: brute prediction --- psyke/__init__.py | 5 +++-- psyke/extraction/hypercubic/__init__.py | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index 9ed1e4ae..c9533ff4 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -72,9 +72,10 @@ def __convert(self, ys: Iterable, mapping: dict[str: int] = None) -> Iterable: def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, mapping: dict[str: int] = None) -> Iterable: - return self.__convert(self._brute_predict(dataframe), mapping) + return self.__convert(self._brute_predict(dataframe, criterion, n, mapping), mapping) - def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, + mapping: dict[str: int] = None) -> Iterable: raise NotImplementedError('brute_predict') def unscale(self, values, name): diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 0db5a8d4..9f9a978e 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -9,7 +9,7 @@ from sklearn.linear_model import LinearRegression from tuprolog.core import Var, Struct, clause from tuprolog.theory import Theory, mutable_theory -from psyke import logger, PedagogicalExtractor +from psyke import logger, PedagogicalExtractor, EvaluableModel from psyke.extraction.hypercubic.hypercube import HyperCube, RegressionCube, ClassificationCube, ClosedCube, Point, \ GenericCube from psyke.utils.logic import create_variable_list, create_head, to_var, Simplifier @@ -18,16 +18,17 @@ from sklearn.neighbors import BallTree -class HyperCubePredictor: +class HyperCubePredictor(EvaluableModel): def __init__(self, output=Target.CONSTANT, normalization=None): + super().__init__(normalization) self._hypercubes = [] self._output = output - self.normalization = normalization def _predict(self, dataframe: pd.DataFrame) -> Iterable: return np.array([self._predict_from_cubes(row.to_dict()) for _, row in dataframe.iterrows()]) - def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2) -> Iterable: + def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, + mapping: dict[str: int] = None) -> Iterable: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] if sum(idx) > 0: From 5b0a98c2b2f758dd82af765feb6dc218a3498854 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:59:55 +0200 Subject: [PATCH 45/67] fix: class hierarchy --- demo/DemoRegression.ipynb | 18 ++++++++++-------- psyke/extraction/hypercubic/__init__.py | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/demo/DemoRegression.ipynb b/demo/DemoRegression.ipynb index 1aa736c0..3bef7b2a 100644 --- a/demo/DemoRegression.ipynb +++ b/demo/DemoRegression.ipynb @@ -181,19 +181,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "COSMiK performance (3 rules with 90.03% coverage):\n", - "MAE = 0.11 (data), 0.11 (BB)\n", - "MSE = 0.05 (data), 0.05 (BB)\n", - "R2 = 0.92 (data), 0.92 (BB)\n", + "COSMiK performance (4 rules with 87.83% coverage):\n", + "MAE = 0.00 (data), 0.00 (BB)\n", + "MSE = 0.00 (data), 0.00 (BB)\n", + "R2 = 1.00 (data), 1.00 (BB)\n", "\n", "COSMiK extracted rules:\n", "\n", "'Z0'(X, Y, 1.0) :-\n", " X in [0.0, 0.48], Y in [0.0, 0.48].\n", - "'Z0'(X, Y, -0.98) :-\n", - " X in [0.65, 1.0], Y in [0.0, 0.46].\n", - "'Z0'(X, Y, -0.13) :-\n", - " X in [0.05, 0.99], Y in [0.59, 1.0].\n" + "'Z0'(X, Y, -1.0) :-\n", + " X in [0.65, 1.0], Y in [0.0, 0.44].\n", + "'Z0'(X, Y, -1.0) :-\n", + " X in [0.90, 1.0], Y in [0.75, 1.0].\n", + "'Z0'(X, Y, 0.0) :-\n", + " X in [0.05, 0.83], Y in [0.57, 0.99].\n" ] } ], diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 9f9a978e..76edcd4a 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -85,8 +85,8 @@ def _get_cube_output(cube, data: dict[str, float]) -> float: class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC): def __init__(self, predictor, output, discretization=None, normalization=None): - PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) HyperCubePredictor.__init__(self, output=output, normalization=normalization) + PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: if self._output == Target.CONSTANT: From 788f7fdcde70a4f5e56d71c51e6610dc046f1662 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:30:17 +0200 Subject: [PATCH 46/67] fix: value conversion --- psyke/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index c9533ff4..dd8c571a 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -67,7 +67,7 @@ def __convert(self, ys: Iterable, mapping: dict[str: int] = None) -> Iterable: ys = [inverse_mapping[y] for y in ys] if self.normalization is not None: m, s = self.normalization[list(self.normalization.keys())[-1]] - ys = [prediction if prediction is None else ys * s + m for prediction in ys] + ys = [prediction if prediction is None else prediction * s + m for prediction in ys] return ys def brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: int = 2, From 7855b684c95ac9b8526c67ecdb5e8c06ed298e09 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:37:53 +0200 Subject: [PATCH 47/67] fix(GridEx): normalization --- psyke/extraction/hypercubic/gridex/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyke/extraction/hypercubic/gridex/__init__.py b/psyke/extraction/hypercubic/gridex/__init__.py index 09272ee0..664554b6 100644 --- a/psyke/extraction/hypercubic/gridex/__init__.py +++ b/psyke/extraction/hypercubic/gridex/__init__.py @@ -17,7 +17,7 @@ class GridEx(HyperCubeExtractor): def __init__(self, predictor, grid: Grid, min_examples: int, threshold: float, normalization=None, seed=get_default_random_seed()): - super().__init__(predictor, Target.CONSTANT, normalization) + super().__init__(predictor, Target.CONSTANT, normalization=normalization) self.grid = grid self.min_examples = min_examples self.threshold = threshold From dccbc626e5acafc1972e7b3fb306dfe0840896dd Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 01:24:09 +0200 Subject: [PATCH 48/67] feat(brute prediction): added default mode --- psyke/__init__.py | 4 ++++ psyke/extraction/hypercubic/__init__.py | 20 +++++++++++++++----- psyke/utils/plot.py | 3 ++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/psyke/__init__.py b/psyke/__init__.py index dd8c571a..d1f0fc0d 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -374,6 +374,10 @@ def __init__(self, predictor, discretization=None, normalization=None): Extractor.__init__(self, predictor=predictor, discretization=discretization, normalization=normalization) def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: + from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube + if isinstance(self, HyperCubeExtractor): + self._surrounding = HyperCube.create_surrounding_cube(dataframe, output=self._output) + self._surrounding.update(dataframe, self.predictor) new_y = self.predictor.predict(dataframe.iloc[:, :-1]) if mapping is not None: if hasattr(new_y[0], 'shape'): diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 76edcd4a..5becc59f 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -32,10 +32,17 @@ def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: predictions = self._predict(dataframe) idx = [prediction is None for prediction in predictions] if sum(idx) > 0: - tree, cubes = self._create_brute_tree(criterion, n) - predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( - row.to_dict(), tree, cubes - ) for _, row in dataframe[idx].iterrows()]) + if criterion == 'default': + if not isinstance(self, HyperCubeExtractor): + raise ValueError("'default' criterion only available for instances of HyperCubeExtractor") + predictions[idx] = np.array([HyperCubePredictor._get_cube_output( + self._surrounding, row + ) for _, row in dataframe[idx].iterrows()]) + else: + tree, cubes = self._create_brute_tree(criterion, n) + predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( + row.to_dict(), tree, cubes + ) for _, row in dataframe[idx].iterrows()]) return np.array(predictions) @staticmethod @@ -55,7 +62,9 @@ def _create_brute_tree(self, criterion: str = 'center', n: int = 2) -> (BallTree elif criterion == 'perimeter': points = [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] else: - raise NotImplementedError("'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density']") + raise NotImplementedError( + "'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density', 'default']" + ) return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True)), \ [point[1] for point in points] @@ -87,6 +96,7 @@ class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC): def __init__(self, predictor, output, discretization=None, normalization=None): HyperCubePredictor.__init__(self, output=output, normalization=normalization) PedagogicalExtractor.__init__(self, predictor, discretization=discretization, normalization=normalization) + self._surrounding = None def _default_cube(self) -> HyperCube | RegressionCube | ClassificationCube: if self._output == Target.CONSTANT: diff --git a/psyke/utils/plot.py b/psyke/utils/plot.py index 5db7a9e8..04be6b96 100644 --- a/psyke/utils/plot.py +++ b/psyke/utils/plot.py @@ -160,6 +160,7 @@ def color_fader(v: float = 0., c1: str = 'green', c2: str = 'red'): pass # ax.text2D(0., 0.88, pretty_theory(theory, new_line=False), transform=ax.transAxes, fontsize=8) if isinstance(ys[0], str): - custom_lines = [Line2D([0], [0], marker='o', markerfacecolor=get_color(c), markersize=20, color='w') for c in classes] + custom_lines = [Line2D([0], [0], marker='o', markerfacecolor=get_color(c), + markersize=20, color='w') for c in classes] ax.legend(custom_lines, classes, loc='upper left', numpoints=1, ncol=3, fontsize=18, bbox_to_anchor=(0, 0)) plt.savefig(output, format='pdf') From 60b7d5ada6440b4299e3fc9268154d2db22ddd26 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 01:52:25 +0200 Subject: [PATCH 49/67] fix(GridEx) --- demo/DemoClassification.ipynb | 51 +++++++++++++++-------------------- psyke/__init__.py | 9 ++++--- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb index 3bd95396..0c802f4f 100644 --- a/demo/DemoClassification.ipynb +++ b/demo/DemoClassification.ipynb @@ -265,29 +265,20 @@ "text": [ "DiViNE performance (3 rules with 73.68% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n" - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/1973727682.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 7\u001B[1;33m \u001B[0mscores\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcompleteness\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdivine\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'density'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 8\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34mf'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 9\u001B[0m \u001B[0mprint_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscores\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_4396/301734117.py\u001B[0m in \u001B[0;36mget_scores\u001B[1;34m(extractor, test, predictor, brute, criterion, n)\u001B[0m\n\u001B[0;32m 6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget_scores\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mextractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbrute\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mFalse\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;34m'density'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 8\u001B[1;33m return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n\u001B[0m\u001B[0;32m 9\u001B[0m [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mscore\u001B[1;34m(self, dataframe, predictor, fidelity, completeness, brute, criterion, n, task, scoring_function)\u001B[0m\n\u001B[0;32m 85\u001B[0m extracted = np.array(\n\u001B[0;32m 86\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[0mbrute\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 87\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 88\u001B[0m )\n\u001B[0;32m 89\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mextracted\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[0mrow\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mto_dict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcubes\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m ) for _, row in dataframe[idx].iterrows()])\n\u001B[1;32m---> 39\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + "F1 = 0.96 (data), 1.00 (BB)\n", + "\n", + "DiViNE brute performance (3 rules with 100.00% coverage):\n", + "Classification accuracy = 0.95 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", + "\n", + "DiViNE extracted rules:\n", + "\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", + " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", + "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", + " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" ] } ], @@ -313,24 +304,24 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 11, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "DiViNE performance (3 rules with 71.05% coverage):\n", + "DiViNE performance (3 rules with 73.68% coverage):\n", "Classification accuracy = 0.96 (data), 1.00 (BB)\n", "F1 = 0.96 (data), 1.00 (BB)\n", "\n", "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", + "Classification accuracy = 0.95 (data), 0.97 (BB)\n", + "F1 = 0.95 (data), 0.97 (BB)\n", "\n", "DiViNE extracted rules:\n", "\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.7], PetalWidth in [1.0, 1.5].\n", + " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", @@ -360,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 12, "outputs": [ { "name": "stdout", @@ -417,7 +408,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 13, "outputs": [ { "name": "stdout", diff --git a/psyke/__init__.py b/psyke/__init__.py index d1f0fc0d..77bb12e8 100644 --- a/psyke/__init__.py +++ b/psyke/__init__.py @@ -375,9 +375,6 @@ def __init__(self, predictor, discretization=None, normalization=None): def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: from psyke.extraction.hypercubic import HyperCubeExtractor, HyperCube - if isinstance(self, HyperCubeExtractor): - self._surrounding = HyperCube.create_surrounding_cube(dataframe, output=self._output) - self._surrounding.update(dataframe, self.predictor) new_y = self.predictor.predict(dataframe.iloc[:, :-1]) if mapping is not None: if hasattr(new_y[0], 'shape'): @@ -390,7 +387,11 @@ def extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: new_y = pd.DataFrame(new_y).set_index(dataframe.index) data = dataframe.iloc[:, :-1].copy().join(new_y) data.columns = dataframe.columns - return self._extract(data, mapping, sort) + theory = self._extract(data, mapping, sort) + if isinstance(self, HyperCubeExtractor): + self._surrounding = HyperCube.create_surrounding_cube(dataframe, output=self._output) + self._surrounding.update(dataframe, self.predictor) + return theory def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort: bool = True) -> Theory: raise NotImplementedError('extract') From 6c6a15c902209ea478bdb53c996413be9a11bbf2 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 2 Jul 2023 16:13:03 +0200 Subject: [PATCH 50/67] feat: removed duplicate points for perimeter-based vicinity --- psyke/extraction/hypercubic/hypercube.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index e7b29479..404430c5 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -276,6 +276,13 @@ def duplicate(point: Point, feature: str) -> Iterable[Point]: new_point_b[feature] = self.get_second(feature) return [new_point_a, new_point_b] + def remove_duplicates(points: Iterable[Point]) -> Iterable[Point]: + new_points = [] + for point in points: + if point not in new_points: + new_points.append(point) + return new_points + def split(point: Point, feature: str, n: int): points = [] for value in np.linspace(self.get_first(feature), self.get_second(feature), n): @@ -291,7 +298,7 @@ def split(point: Point, feature: str, n: int): new_points = np.array([duplicate(point, secondary) if primary != secondary else split(point, primary, n) for point in new_points]).flatten() points = points + list(new_points) - return points + return remove_duplicates(points) def is_adjacent(self, cube: HyperCube) -> str | None: adjacent = None From 30c1f18d2469f205e7df79578777e99aecd1e267 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:11:39 +0200 Subject: [PATCH 51/67] wip --- demo/DemoClassification.ipynb | 691 --------------------- demo/DemoClassificationDisc.ipynb | 452 -------------- demo/DemoRegressionScaled.ipynb | 990 +++++------------------------- demo/README.md | 40 -- demo/demo.py | 27 - 5 files changed, 138 insertions(+), 2062 deletions(-) delete mode 100644 demo/DemoClassification.ipynb delete mode 100644 demo/DemoClassificationDisc.ipynb delete mode 100644 demo/README.md delete mode 100644 demo/demo.py diff --git a/demo/DemoClassification.ipynb b/demo/DemoClassification.ipynb deleted file mode 100644 index 0c802f4f..00000000 --- a/demo/DemoClassification.ipynb +++ /dev/null @@ -1,691 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "# PSyKE's demo\n", - "\n", - "Some imports." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "6b710e7c", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.model_selection import train_test_split\n", - "from sklearn.datasets import load_iris\n", - "import pandas as pd\n", - "\n", - "from sklearn.neighbors import KNeighborsClassifier\n", - "from sklearn.metrics import accuracy_score, f1_score\n", - "\n", - "from psyke import Extractor, Clustering, EvaluableModel\n", - "from psyke.extraction.hypercubic.strategy import AdaptiveStrategy\n", - "from psyke.extraction.hypercubic import Grid, FeatureRanker\n", - "from psyke.tuning.orchid import OrCHiD\n", - "from psyke.utils.logic import pretty_theory\n", - "from psyke.utils import Target" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "Import iris dataset separating features and class." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "x, y = load_iris(return_X_y=True, as_frame=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "Rename of the features." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "38d5afb0", - "metadata": {}, - "outputs": [], - "source": [ - "x.columns = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "Replace integer indices with the corresponding string class." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4f807185", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " target\n0 setosa\n1 setosa\n2 setosa\n3 setosa\n4 setosa\n.. ...\n145 virginica\n146 virginica\n147 virginica\n148 virginica\n149 virginica\n\n[150 rows x 1 columns]", - "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
target
0setosa
1setosa
2setosa
3setosa
4setosa
......
145virginica
146virginica
147virginica
148virginica
149virginica
\n

150 rows × 1 columns

\n
" - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = pd.DataFrame(y).replace({\"target\": {0: 'setosa', 1: 'versicolor', 2: 'virginica'}})\n", - "y" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "The final dataset:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "7ac49b4e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " SepalLength SepalWidth PetalLength PetalWidth iris\n0 5.1 3.5 1.4 0.2 setosa\n1 4.9 3.0 1.4 0.2 setosa\n2 4.7 3.2 1.3 0.2 setosa\n3 4.6 3.1 1.5 0.2 setosa\n4 5.0 3.6 1.4 0.2 setosa\n.. ... ... ... ... ...\n145 6.7 3.0 5.2 2.3 virginica\n146 6.3 2.5 5.0 1.9 virginica\n147 6.5 3.0 5.2 2.0 virginica\n148 6.2 3.4 5.4 2.3 virginica\n149 5.9 3.0 5.1 1.8 virginica\n\n[150 rows x 5 columns]", - "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
SepalLengthSepalWidthPetalLengthPetalWidthiris
05.13.51.40.2setosa
14.93.01.40.2setosa
24.73.21.30.2setosa
34.63.11.50.2setosa
45.03.61.40.2setosa
..................
1456.73.05.22.3virginica
1466.32.55.01.9virginica
1476.53.05.22.0virginica
1486.23.45.42.3virginica
1495.93.05.11.8virginica
\n

150 rows × 5 columns

\n
" - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dataset = x.join(y)\n", - "dataset.columns = [*dataset.columns[:-1], 'iris']\n", - "dataset" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "Split between train and test set in a reproducible way." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "03fc5e2c", - "metadata": {}, - "outputs": [], - "source": [ - "train, test = train_test_split(dataset, test_size=0.25, random_state=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false - }, - "source": [ - "We use as predictor a KNN and we train it." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "aa8a3128", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Accuracy: 0.97\n", - "F1: 0.97\n" - ] - } - ], - "source": [ - "#predictor = MLPClassifier(alpha=1, max_iter=1000)\n", - "predictor = KNeighborsClassifier(n_neighbors=7)\n", - "#predictor = DecisionTreeClassifier()\n", - "predictor.fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "print(f'Accuracy: {accuracy_score(predictor.predict(test.iloc[:, :-1]), test.iloc[:, -1]):.2f}')\n", - "print(f'F1: {f1_score(predictor.predict(test.iloc[:, :-1]), test.iloc[:, -1], average=\"weighted\"):.2f}')" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [], - "source": [ - "def print_scores(scores):\n", - " print(f'Classification accuracy = {scores[EvaluableModel.ClassificationScore.ACCURACY][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.ACCURACY][1]:.2f} (BB)\\n'\n", - " f'F1 = {scores[EvaluableModel.ClassificationScore.F1][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.F1][1]:.2f} (BB)')\n", - "\n", - "def get_scores(extractor, test, predictor, brute=False, criterion='density', n=2):\n", - " return extractor.score(test, predictor, True, True, brute, criterion, n, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "We create an extractor that uses the CART algorithm and we extract prolog rules from our trained KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 9, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "CART extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength =< 2.6.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength =< 4.75.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 10, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DiViNE performance (3 rules with 73.68% coverage):\n", - "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "\n", - "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "DiViNE extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" - ] - } - ], - "source": [ - "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=True)\n", - "theory_from_divine = divine.extract(train)\n", - "scores, completeness = get_scores(divine, test, predictor)\n", - "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(divine, test, predictor, True, 'density')\n", - "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 11, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DiViNE performance (3 rules with 73.68% coverage):\n", - "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "\n", - "DiViNE brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "DiViNE extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.9, 7.0], SepalWidth in [2.0, 3.2], PetalLength in [3.3, 4.9], PetalWidth in [1.0, 1.5].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.3, 5.7], SepalWidth in [2.3, 4.4], PetalLength in [1.0, 1.9], PetalWidth in [0.1, 0.6].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [5.6, 7.7], SepalWidth in [2.5, 3.8], PetalLength in [4.8, 6.9], PetalWidth in [1.4, 2.5].\n" - ] - } - ], - "source": [ - "divine = Extractor.divine(predictor, k=5, patience=15, close_to_center=False)\n", - "theory_from_divine = divine.extract(train)\n", - "scores, completeness = get_scores(divine, test, predictor)\n", - "print(f'DiViNE performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(divine, test, predictor, True)\n", - "print(f'\\nDiViNE brute performance ({divine.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nDiViNE extracted rules:\\n\\n' + pretty_theory(theory_from_divine))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 12, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ITER performance (3 rules with 97.37% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "ITER brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.97 (data), 1.00 (BB)\n", - "F1 = 0.97 (data), 1.00 (BB)\n", - "\n", - "ITER extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [0.99, 2.58], PetalWidth in [0.09, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [2.58, 4.94], PetalWidth in [0.09, 2.50].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " SepalLength in [4.29, 7.70], SepalWidth in [1.99, 4.40], PetalLength in [4.94, 6.90], PetalWidth in [0.09, 2.50].\n" - ] - } - ], - "source": [ - "it = Extractor.iter(predictor, min_update=0.2, min_examples=150, threshold=0.1,\n", - " max_iterations=100, n_points=1, fill_gaps=True)\n", - "theory_from_iter = it.extract(train)\n", - "scores, completeness = get_scores(it, test, predictor)\n", - "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(it, test, predictor, True)\n", - "print(f'\\nITER brute performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "We create a GridEx extractor to extract prolog rules from the same KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 13, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GridEx performance (5 rules with 94.74% coverage):\n", - "Classification accuracy = 0.94 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "GridEx brute performance (5 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.97 (BB)\n", - "F1 = 0.95 (data), 0.97 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [4.54, 5.72], PetalWidth in [1.06, 1.54].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 2.17], PetalWidth in [0.09, 1.06].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [2.17, 4.54], PetalWidth in [0.57, 1.06].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.36, 4.54], PetalWidth in [1.06, 2.02].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.54, 6.90], PetalWidth in [1.54, 2.50].\n" - ] - } - ], - "source": [ - "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", - "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.7, 5)])), threshold=.1, min_examples=1)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(gridEx, test, predictor, True)\n", - "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 41, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GridEx performance (3 rules with 94.74% coverage):\n", - "Classification accuracy = 0.92 (data), 0.94 (BB)\n", - "F1 = 0.92 (data), 0.95 (BB)\n", - "\n", - "GridEx brute performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.92 (data), 0.95 (BB)\n", - "F1 = 0.92 (data), 0.95 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalLength in [0.99, 2.47].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength in [3.21, 4.68].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength in [4.68, 6.90].\n" - ] - } - ], - "source": [ - "ranked = FeatureRanker(x.columns).fit(predictor, x).rankings()\n", - "gridEx = Extractor.gridex(predictor, Grid(1, AdaptiveStrategy(ranked, [(0.85, 8)])), threshold=.1, min_examples=1)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "scores, completeness = get_scores(gridEx, test, predictor, True)\n", - "print(f'\\nGridEx brute performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "We use the CReEPy clustering-based extractor to perform the extraction." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "def print_clustering_scores(scores):\n", - " print(f'ARI = {scores[EvaluableModel.ClusteringScore.ARI][0]:.2f}\\n'\n", - " f'AMI = {scores[EvaluableModel.ClusteringScore.AMI][0]:.2f}\\n'\n", - " f'V-measure = {scores[EvaluableModel.ClusteringScore.V][0]:.2f}\\n'\n", - " f'FMI = {scores[EvaluableModel.ClusteringScore.FMI][0]:.2f}')\n", - "\n", - "def get_clustering_scores(clustering, test):\n", - " return clustering.score(test, None, False, True, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClusteringScore.ARI, EvaluableModel.ClusteringScore.AMI,\n", - " EvaluableModel.ClusteringScore.V, EvaluableModel.ClusteringScore.FMI])\n", - "\n", - "def print_scores_short(scores):\n", - " print(f'Classification accuracy = {scores[EvaluableModel.ClassificationScore.ACCURACY][0]:.2f}')\n", - "\n", - "def get_scores_short(extractor, test):\n", - " return extractor.score(test, None, False, True, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.ExACT, output=Target.CLASSIFICATION,\n", - " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", - "orchid.search()\n", - "(_, _, depth, threshold) = orchid.get_best()[0]" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "exact = Clustering.exact(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", - "exact.fit(train)\n", - "scores, completeness = get_clustering_scores(exact, test)\n", - "print(f'ExACT performance ({exact.n_rules} clusters with {completeness * 100:.2f}% coverage):')\n", - "print_clustering_scores(scores)\n", - "scores, _ = get_scores_short(exact, test)\n", - "print_scores_short(scores)\n", - "print()\n", - "exact.explain()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", - " ranks=ranked, ignore_threshold=.99, clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.CREAM, output=Target.CLASSIFICATION,\n", - " max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", - "orchid.search()\n", - "(_, _, depth, threshold) = orchid.get_best()[0]" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "cream = Clustering.cream(depth=depth, error_threshold=threshold, output=Target.CLASSIFICATION)\n", - "cream.fit(train)\n", - "scores, completeness = get_clustering_scores(cream, test)\n", - "print(f'CREAM performance ({cream.n_rules} clusters with {completeness * 100:.2f}% coverage):')\n", - "print_clustering_scores(scores)\n", - "scores, _ = get_scores_short(cream, test)\n", - "print_scores_short(scores)\n", - "print()\n", - "cream.explain()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.1, output=Target.CLASSIFICATION,\n", - " ranks=ranked, ignore_threshold=.99, clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/demo/DemoClassificationDisc.ipynb b/demo/DemoClassificationDisc.ipynb deleted file mode 100644 index c240351b..00000000 --- a/demo/DemoClassificationDisc.ipynb +++ /dev/null @@ -1,452 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# PSyKE's demo\n", - "\n", - "Some imports." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "6b710e7c", - "metadata": {}, - "outputs": [], - "source": [ - "from psyke.utils.dataframe import get_discrete_features_supervised, get_discrete_dataset\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.datasets import load_iris\n", - "import pandas as pd\n", - "\n", - "from sklearn.neighbors import KNeighborsClassifier\n", - "from sklearn.tree import DecisionTreeClassifier\n", - "from psyke.extraction.cart.predictor import CartPredictor\n", - "\n", - "from psyke import Extractor, EvaluableModel\n", - "from psyke.utils.logic import pretty_theory" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Import iris dataset separating features and class." - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "x, y = load_iris(return_X_y=True, as_frame=True)" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Rename of the features." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "38d5afb0", - "metadata": {}, - "outputs": [], - "source": [ - "x.columns = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']" - ] - }, - { - "cell_type": "markdown", - "source": [ - "The original features' dataset is discretized using the equal frequency method. Each feature is mapped in a 3 (can be an arbitrary integer) new one-hot encoded sub-features representing 3 real intervals. So from the original 4 features we have a new 12 features dataset. S, M and L stand for small, medium and large." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "423ff1b4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SepalLength = {'SepalLength_0' if SepalLength ∈ ]-∞, 5.39], 'SepalLength_1' if SepalLength ∈ [5.39, 6.26[, 'SepalLength_2' if SepalLength ∈ ]6.26, ∞[}\n", - "\n", - "SepalWidth = {'SepalWidth_0' if SepalWidth ∈ ]-∞, 2.87], 'SepalWidth_1' if SepalWidth ∈ [2.87, 3.20[, 'SepalWidth_2' if SepalWidth ∈ ]3.20, ∞[}\n", - "\n", - "PetalLength = {'PetalLength_0' if PetalLength ∈ ]-∞, 2.27], 'PetalLength_1' if PetalLength ∈ [2.27, 4.87[, 'PetalLength_2' if PetalLength ∈ ]4.87, ∞[}\n", - "\n", - "PetalWidth = {'PetalWidth_0' if PetalWidth ∈ ]-∞, 0.65], 'PetalWidth_1' if PetalWidth ∈ [0.65, 1.64[, 'PetalWidth_2' if PetalWidth ∈ ]1.64, ∞[}\n", - "\n" - ] - } - ], - "source": [ - "iris_features = get_discrete_features_supervised(x.join(y))\n", - "\n", - "for descrete_feature in iris_features:\n", - " print(str(descrete_feature), end='\\n\\n')" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Reassign features' data to the discretized one." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "ffc1852e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " PetalLength_0 PetalLength_1 PetalLength_2 PetalWidth_0 PetalWidth_1 \\\n0 1 0 0 1 0 \n1 1 0 0 1 0 \n2 1 0 0 1 0 \n3 1 0 0 1 0 \n4 1 0 0 1 0 \n.. ... ... ... ... ... \n145 0 0 1 0 0 \n146 0 0 1 0 0 \n147 0 0 1 0 0 \n148 0 0 1 0 0 \n149 0 0 1 0 0 \n\n PetalWidth_2 SepalLength_0 SepalLength_1 SepalLength_2 SepalWidth_0 \\\n0 0 1 0 0 0 \n1 0 1 0 0 0 \n2 0 1 0 0 0 \n3 0 1 0 0 0 \n4 0 1 0 0 0 \n.. ... ... ... ... ... \n145 1 0 0 1 0 \n146 1 0 0 1 1 \n147 1 0 0 1 0 \n148 1 0 1 0 0 \n149 1 0 1 0 0 \n\n SepalWidth_1 SepalWidth_2 \n0 0 1 \n1 1 0 \n2 1 0 \n3 1 0 \n4 0 1 \n.. ... ... \n145 1 0 \n146 0 0 \n147 1 0 \n148 0 1 \n149 1 0 \n\n[150 rows x 12 columns]", - "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
PetalLength_0PetalLength_1PetalLength_2PetalWidth_0PetalWidth_1PetalWidth_2SepalLength_0SepalLength_1SepalLength_2SepalWidth_0SepalWidth_1SepalWidth_2
0100100100001
1100100100010
2100100100010
3100100100010
4100100100001
.......................................
145001001001010
146001001001100
147001001001010
148001001010001
149001001010010
\n

150 rows × 12 columns

\n
" - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = get_discrete_dataset(x, iris_features)\n", - "x" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Replace integer indices with the corresponding string class." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "4f807185", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " target\n0 setosa\n1 setosa\n2 setosa\n3 setosa\n4 setosa\n.. ...\n145 virginica\n146 virginica\n147 virginica\n148 virginica\n149 virginica\n\n[150 rows x 1 columns]", - "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
target
0setosa
1setosa
2setosa
3setosa
4setosa
......
145virginica
146virginica
147virginica
148virginica
149virginica
\n

150 rows × 1 columns

\n
" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y = pd.DataFrame(y).replace({\"target\": {0: 'setosa', 1: 'versicolor', 2: 'virginica'}})\n", - "y" - ] - }, - { - "cell_type": "markdown", - "source": [ - "The final dataset:" - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "7ac49b4e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": " PetalLength_0 PetalLength_1 PetalLength_2 PetalWidth_0 PetalWidth_1 \\\n0 1 0 0 1 0 \n1 1 0 0 1 0 \n2 1 0 0 1 0 \n3 1 0 0 1 0 \n4 1 0 0 1 0 \n.. ... ... ... ... ... \n145 0 0 1 0 0 \n146 0 0 1 0 0 \n147 0 0 1 0 0 \n148 0 0 1 0 0 \n149 0 0 1 0 0 \n\n PetalWidth_2 SepalLength_0 SepalLength_1 SepalLength_2 SepalWidth_0 \\\n0 0 1 0 0 0 \n1 0 1 0 0 0 \n2 0 1 0 0 0 \n3 0 1 0 0 0 \n4 0 1 0 0 0 \n.. ... ... ... ... ... \n145 1 0 0 1 0 \n146 1 0 0 1 1 \n147 1 0 0 1 0 \n148 1 0 1 0 0 \n149 1 0 1 0 0 \n\n SepalWidth_1 SepalWidth_2 iris \n0 0 1 setosa \n1 1 0 setosa \n2 1 0 setosa \n3 1 0 setosa \n4 0 1 setosa \n.. ... ... ... \n145 1 0 virginica \n146 0 0 virginica \n147 1 0 virginica \n148 0 1 virginica \n149 1 0 virginica \n\n[150 rows x 13 columns]", - "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
PetalLength_0PetalLength_1PetalLength_2PetalWidth_0PetalWidth_1PetalWidth_2SepalLength_0SepalLength_1SepalLength_2SepalWidth_0SepalWidth_1SepalWidth_2iris
0100100100001setosa
1100100100010setosa
2100100100010setosa
3100100100010setosa
4100100100001setosa
..........................................
145001001001010virginica
146001001001100virginica
147001001001010virginica
148001001010001virginica
149001001010010virginica
\n

150 rows × 13 columns

\n
" - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dataset = x.join(y)\n", - "dataset.columns = [*dataset.columns[:-1], 'iris']\n", - "dataset" - ] - }, - { - "cell_type": "markdown", - "source": [ - "Split between train and test set in a reproducible way." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "03fc5e2c", - "metadata": {}, - "outputs": [], - "source": [ - "train, test = train_test_split(dataset, test_size=0.5, random_state=0)" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We use as predictor a KNN with K = 4 and we train it." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "aa8a3128", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": "0.9333333333333333" - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "predictor = KNeighborsClassifier(n_neighbors=4)\n", - "predictor.fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "predictor.score(test.iloc[:, :-1], test.iloc[:, -1])" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We create an extractor that uses the REAL algorithm and we extract prolog rules from our trained KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 19, - "outputs": [], - "source": [ - "def print_scores(scores):\n", - " print(f'Classification accuracy = {scores[EvaluableModel.ClassificationScore.ACCURACY][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.ACCURACY][1]:.2f} (BB)\\n'\n", - " f'F1 = {scores[EvaluableModel.ClassificationScore.F1][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.ClassificationScore.F1][1]:.2f} (BB)')\n", - "\n", - "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, EvaluableModel.Task.CLASSIFICATION,\n", - " [EvaluableModel.ClassificationScore.ACCURACY, EvaluableModel.ClassificationScore.F1])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "5e97565d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "REAL performance (8 rules with 94.67% coverage):\n", - "Classification accuracy = 0.96 (data), 1.00 (BB)\n", - "F1 = 0.96 (data), 1.00 (BB)\n", - "REAL extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalWidth =< 0.64, SepalLength =< 5.38.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalWidth =< 0.64, SepalWidth > 3.20, SepalLength =< 6.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalWidth in [0.64, 1.63], SepalWidth =< 2.87, PetalLength > 2.26, SepalLength =< 6.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalWidth in [0.64, 1.63], SepalLength in [5.38, 6.26], SepalWidth =< 3.20.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalWidth in [0.64, 1.63], PetalLength =< 4.86, SepalLength > 5.38, SepalWidth =< 3.20.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " SepalLength in [5.38, 6.26], SepalWidth in [2.87, 3.20], PetalLength =< 4.86, PetalWidth > 0.64.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength > 4.86, SepalLength > 6.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength > 4.86, PetalWidth > 1.63.\n" - ] - } - ], - "source": [ - "real = Extractor.real(predictor, iris_features)\n", - "theory_from_real = real.extract(train)\n", - "scores, completeness = get_scores(real, test, predictor)\n", - "print(f'REAL performance ({real.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('REAL extracted rules:\\n\\n' + pretty_theory(theory_from_real))" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We create a different extractor that use Trepan algorithm and we extract prolog rules from the same KNN." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "dc20410e", - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "TREPAN performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.96 (BB)\n", - "F1 = 0.95 (data), 0.96 (BB)\n", - "\n", - "Trepan extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor) :-\n", - " PetalLength > 2.26, PetalLength in [2.26, 4.86].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength > 2.26.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa).\n" - ] - } - ], - "source": [ - "trepan = Extractor.trepan(predictor, iris_features)\n", - "theory_from_trepan = trepan.extract(train)\n", - "scores, completeness = get_scores(trepan, test, predictor)\n", - "print(f'TREPAN performance ({trepan.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nTrepan extracted rules:\\n\\n' + pretty_theory(theory_from_trepan))" - ] - }, - { - "cell_type": "markdown", - "source": [ - "We create another different extractor that use CART algorithm." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 22, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (3 rules with 100.00% coverage):\n", - "Classification accuracy = 0.95 (data), 0.96 (BB)\n", - "F1 = 0.95 (data), 0.96 (BB)\n", - "\n", - "CART extracted rules:\n", - "\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, setosa) :-\n", - " PetalWidth =< 0.64.\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, virginica) :-\n", - " PetalLength not_in [2.26, 4.86].\n", - "iris(PetalLength, PetalWidth, SepalLength, SepalWidth, versicolor).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, discretization=iris_features, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 98331ff5..28f7f265 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -1,15 +1,5 @@ { "cells": [ - { - "cell_type": "markdown", - "id": "f52126f3", - "metadata": {}, - "source": [ - "# PSyKE's demo for regression tasks\n", - "\n", - "Some imports." - ] - }, { "cell_type": "code", "execution_count": 1, @@ -17,153 +7,78 @@ "metadata": {}, "outputs": [], "source": [ - "from psyke import Extractor, Clustering\n", - "from psyke.tuning.pedro import PEDRO\n", - "from psyke.tuning import Objective\n", - "from psyke.tuning.crash import CRASH\n", - "from sklearn.tree import DecisionTreeRegressor\n", - "from psyke.utils.logic import pretty_theory\n", - "from psyke.utils.metrics import mae, mse, r2\n", - "from sklearn.model_selection import train_test_split\n", + "from sklearn.neighbors import KNeighborsRegressor as KNN\n", "from sklearn.preprocessing import StandardScaler\n", - "from psyke.utils import Target\n", - "import pandas as pd" - ] - }, - { - "cell_type": "markdown", - "id": "d7c90ed2", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "Import an artificial dataset." + "\n", + "from joblib import dump, load\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "from psyke import Extractor, Clustering, Target\n", + "from psyke.extraction.hypercubic.strategy import AdaptiveStrategy\n", + "from psyke.extraction.hypercubic import Grid, FeatureRanker\n", + "from psyke.utils.logic import pretty_theory" ] }, { "cell_type": "code", "execution_count": 2, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "#dataset = pd.read_csv(\"../test/resources/datasets/df.csv\")\n", - "#dataset = dataset[[\"X\", \"Y\", \"Z4\"]].dropna()\n", - "#dataset = pd.read_csv(\"../test/resources/datasets/CCPP.csv\", sep=\";\", decimal=\",\")\n", - "dataset = pd.read_csv(\"../test/resources/datasets/arti.csv\")" - ] - }, - { - "cell_type": "markdown", - "id": "d673b766", - "metadata": {}, - "source": [ - "Split between train and test set in a reproducible way." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "03fc5e2c", - "metadata": {}, "outputs": [], "source": [ - "train, test = train_test_split(dataset, test_size=0.5, random_state=10)\n", + "def getTrainTest(data, testB):\n", + " b = bartels[bartels.n==testB]\n", + " t0, t1 = b.t0.values[0], b.t1.values[0]\n", + " idx = (data.index >= t0) & (data.index < t1)\n", + " return data[~idx], data[idx]\n", "\n", - "scaler = StandardScaler().fit(train)\n", - "train = pd.DataFrame(scaler.transform(train), columns=train.columns, index=train.index)\n", - "test = pd.DataFrame(scaler.transform(test), columns=test.columns, index=test.index)\n", - "\n", - "normalization = {key: (m, s) for key, m, s in zip(train.columns, scaler.mean_, scaler.scale_)}" - ] - }, - { - "cell_type": "markdown", - "id": "fa6754a0", - "metadata": {}, - "source": [ - "We select and train a predictor." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "bed764ca", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MAE = 0.00\n", - "MSE = 0.00\n", - "R2 = 1.00\n" - ] - } + "def getScaler(train, name):\n", + " scaler = StandardScaler().fit(train)\n", + " dump(scaler, f\"scalers/scalerV{name}.joblib\")\n", + " normalization = {key: (m, s) for key, m, s in zip(train.columns, scaler.mean_, scaler.scale_)}\n", + " return scaler, pd.DataFrame(scaler.transform(train), columns=train.columns), normalization" ], - "source": [ - "#predictor = KNeighborsRegressor(n_neighbors=3).fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "predictor = DecisionTreeRegressor().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "#predictor = LinearRegression().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "\n", - "m, s = normalization[test.columns[-1]]\n", - "\n", - "predicted = predictor.predict(test.iloc[:, :-1]).flatten() * s + m\n", - "true = test.iloc[:, -1] * s + m\n", - "\n", - "print(f'MAE = {mae(true, predicted):.2f}')\n", - "print(f'MSE = {mse(true, predicted):.2f}')\n", - "print(f'R2 = {r2(true, predicted):.2f}')" - ] - }, - { - "cell_type": "markdown", "metadata": { "collapsed": false, "pycharm": { - "name": "#%% md\n" + "name": "#%%\n" } - }, - "source": [ - "We define a function to print the extractors’ evaluation" - ] + } }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "execution_count": 3, "outputs": [], "source": [ - "def evaluate(name, extractor, true, predicted):\n", - " extracted = extractor.unscale(extractor.predict(test.iloc[:, :-1]), test.columns[-1])\n", - " print(f'{name} performance ({extractor.n_rules} rules):\\n'\n", - " f'MAE = {mae(true, extracted):.2f}\\nMAE fidelity = {mae(predicted, extracted):.2f}\\n'\n", - " f'R2 = {r2(true, extracted):.2f}\\nR2 fidelity = {r2(predicted, extracted):.2f}\\n')" - ] - }, - { - "cell_type": "markdown", - "id": "96835867", - "metadata": {}, - "source": [ - "We create several extractors that use ITER, GridEx and GridREx algorithms to extract prolog rules from the predictor." - ] - }, - { - "cell_type": "code", - "execution_count": 6, + "def gridrex(model, train, test, normalization, s, m):\n", + " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", + " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n", + " threshold=5, min_examples=1, normalization=normalization)\n", + " gridREx.extract(train)\n", + " return gridREx.brute_predict(test) * s + m, gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", + "\n", + "def cart(model, train, test, normalization, s, m):\n", + " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", + " CART.extract(train)\n", + " return CART.predict(test) * s + m, CART.n_rules, 0\n", + "\n", + "def cosmik(model, train, test, normalization, s, m):\n", + " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", + " output=Target.CONSTANT, normalization=normalization)\n", + " COSMiK.extract(train)\n", + " return COSMiK.brute_predict(test) * s + m, COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", + "\n", + "def creepy(model, train, test, normalization, s, m):\n", + " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", + " output=Target.CONSTANT, normalization=normalization)\n", + " CReEPy.extract(train)\n", + " return CReEPy.brute_predict(test) * s + m, CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } +<<<<<<< HEAD }, "outputs": [ { @@ -196,787 +111,158 @@ "evaluate('ITER', it, true, predicted)\n", "print('ITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" ] +======= + } +>>>>>>> wip }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "execution_count": 5, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "CReEPy performance (3 rules):\n", - "MAE = 0.08\n", - "MAE fidelity = 0.08\n", - "R2 = 0.81\n", - "R2 fidelity = 0.81\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.99].\n", - "z(X, Y, 0.145238).\n" + "2491 CART\rEx2491 CReEPy\r" + ] + }, + { + "ename": "TypeError", + "evalue": "'function' object is not subscriptable", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/2296165336.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/1333199017.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 20\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n\u001B[0;32m 21\u001B[0m output=Target.CONSTANT, normalization=normalization)\n\u001B[1;32m---> 22\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 23\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\creepy\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdimension\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdimension\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 39\u001B[1;33m \u001B[0mtheory\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[0mlast_clause\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclauses\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mretract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mlast_clause\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_create_theory\u001B[1;34m(self, dataframe, sort)\u001B[0m\n\u001B[0;32m 118\u001B[0m \u001B[0mvariables\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mto_var\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 119\u001B[0m head = HyperCubeExtractor._create_head(dataframe, list(variables.values()),\n\u001B[1;32m--> 120\u001B[1;33m self.unscale(cube.output, dataframe.columns[-1]))\n\u001B[0m\u001B[0;32m 121\u001B[0m \u001B[0mbody\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbody\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mvariables\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0munscale\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[0mnew_theory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0massertZ\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mclause\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mhead\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbody\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36munscale\u001B[1;34m(self, values, name)\u001B[0m\n\u001B[0;32m 76\u001B[0m value * self.normalization[name][1] + self.normalization[name][0] for value in values]\n\u001B[0;32m 77\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 78\u001B[1;33m \u001B[0mvalues\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mvalues\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 79\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mvalues\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 80\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mTypeError\u001B[0m: 'function' object is not subscriptable" ] } ], "source": [ - "creepy = Extractor.creepy(predictor, depth=3, error_threshold=0.02, output=Target.CONSTANT,\n", - " normalization=normalization, clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 8, + "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", + "\n", + "TESTB = [i for i in range(2491, 2509)]\n", + "\n", + "predicted = {'index': [], 'V': [], 'model': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "\n", + "rules = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "\n", + "missed = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "\n", + "for testB in TESTB:\n", + " rules['BR'].append(testB)\n", + " missed['BR'].append(testB)\n", + " print(testB, end='\\r')\n", + "\n", + " data = pd.read_csv(f'data/halffuzzycoefs2B.csv', parse_dates=[0], index_col=0)\n", + " train, test = getTrainTest(data, testB)\n", + "\n", + " predicted['index'] += list(test.index.values)\n", + " predicted['V'] += list(test.V.values)\n", + "\n", + " scaler, scaledTrain, normalization = getScaler(train, f\"test{testB}\")\n", + " scaledTest = pd.DataFrame(scaler.transform(test), columns=test.columns).iloc[:, :-1]\n", + " m, s = normalization[test.columns[-1]]\n", + "\n", + " model = KNN(200, weights='distance', p=1).fit(scaledTrain.iloc[:, :-1], scaledTrain.iloc[:, -1])\n", + " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", + " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", + "\n", + " for name, fun in zip(['GridREx', 'CART', 'CReEPy'], [gridrex, cart, creepy]):\n", + " print(testB, name, end='\\r')\n", + " #if name in ['GridREx', 'CART', 'COSMiK']:\n", + " # continue\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", + " predicted[name] += list(pred)\n", + " rules[name].append(n)\n", + " missed[name].append(miss)" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules):\n", - "MAE = 0.03\n", - "MAE fidelity = 0.03\n", - "R2 = 0.94\n", - "R2 fidelity = 0.94\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.49], Z is 0.7.\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.49], Y in [0.00, 0.99], Z is 0.4.\n", - "z(X, Y, Z) :-\n", - " Y in [0.00, 0.99], Z is 0.38 - 0.09 * X - 1.73 * Y.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=3, error_threshold=0.02, output=Target.REGRESSION,\n", - " normalization=normalization, clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] + } }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, + "outputs": [], + "source": [ + "pd.DataFrame(predicted)" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (4 rules):\n", - "MAE = 0.01\n", - "MAE fidelity = 0.01\n", - "R2 = 0.94\n", - "R2 fidelity = 0.94\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [0.51, 0.99], Y in [0.00, 0.49], Z is 0.3.\n", - "z(X, Y, Z) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.98], Z is 0.0.\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.49], Y in [0.50, 0.99], Z is 0.4.\n", - "z(X, Y, Z) :-\n", - " Y in [0.00, 0.99], Z is 0.7.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.02, output=Target.REGRESSION,\n", - " normalization=normalization, clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] + } }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, + "outputs": [], + "source": [ + "pd.DataFrame(rules)" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 0.06, 2 rules\n", - "\n", - "**********************\n", - "Best Algorithm.ExACT\n", - "**********************\n", - "MAE = 0.06, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.06, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.06, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "CReEPy performance (3 rules):\n", - "MAE = 0.69\n", - "MAE fidelity = 0.69\n", - "R2 = -8.82\n", - "R2 fidelity = -8.82\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [-1.70, 0.01], Y in [-1.69, 0.02], Z is 1.37.\n", - "z(X, Y, Z) :-\n", - " X in [-1.70, 0.01], Y in [-1.69, 1.75], Z is 0.19.\n", - "z(X, Y, Z) :-\n", - " Y in [-1.69, 1.75], Z is -0.76 - 0.02 * X - 0.50 * Y.\n" - ] - } - ], - "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.5,\n", - " algorithm=CRASH.Algorithm.ExACT, output=Target.REGRESSION, normalization=normalization)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] + } }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, + "execution_count": 6, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 0.12, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 0.12, 2 rules\n", - "\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 0.02, 3 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 0.02, 3 rules\n", - "\n", - "**********************\n", - "Best Algorithm.CREAM\n", - "**********************\n", - "MAE = 0.02, 3 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.02, 3 rules\n", - "Threshold = 0.00\n", - "Depth = 2\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.12, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 1\n", - "\n", - "CReEPy performance (4 rules):\n", - "MAE = 0.69\n", - "MAE fidelity = 0.69\n", - "R2 = -9.40\n", - "R2 fidelity = -9.40\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "z(X, Y, Z) :-\n", - " X in [0.07, 1.75], Y in [-1.69, 0.00], Z is -0.19.\n", - "z(X, Y, Z) :-\n", - " X in [0.02, 1.75], Y in [-1.69, 1.71], Z is -1.37.\n", - "z(X, Y, Z) :-\n", - " X in [-1.69, 0.01], Y in [0.03, 1.75], Z is 0.19.\n", - "z(X, Y, Z) :-\n", - " Y in [-1.69, 1.75], Z is 1.37.\n" - ] + "data": { + "text/plain": "{'Bmin': (2.909256272401434, 0.9536815020973909),\n 'Bmedian': (5.282275985663082, 1.4553994817336167),\n 'Bmax': (10.302267025089607, 4.267392124190232),\n 'Btrend': (0.0003254668720864083, 0.05779357910523391),\n 'Bhalf1': (2.92125578628164e-05, 0.10419543281233311),\n 'Bhalf2': (0.0005042227155979311, 0.10530984379227255),\n 'GCRmin': (-3.524847659251415, 1.5620135919428861),\n 'GCRmedian': (-0.013669578853476324, 1.6346771275509167),\n 'GCRmax': (3.732416265082763, 2.1377962993128765),\n 'GCRtrend': (-2.9804500565074855e-05, 0.019833671296497202),\n 'GCRhalf1': (0.0002589125691562307, 0.03025486059161083),\n 'GCRhalf2': (-0.00023048455270716005, 0.030366014637694866),\n 'V': (453.97293906810035, 108.90956420129501)}" + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.75, algorithm=CRASH.Algorithm.CREAM,\n", - " normalization=normalization)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "evaluate('CReEPy', creepy, true, predicted)\n", - "print('CReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, + "normalization" + ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 0.00, 4 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 0.00, 8 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 0.00, 17 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 0.00, 26 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 0.00, 28 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 0.00, 30 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 0.00, 33 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 0.00, 36 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 0.00, 41 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 0.00, 46 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 0.00, 56 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 0.00, 66 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 0.00, 72 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 0.00, 78 rules\n", - "\n", - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.00, 72 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridEx performance (82 rules):\n", - "MAE = 0.00\n", - "MAE fidelity = 0.00\n", - "R2 = 0.99\n", - "R2 fidelity = 0.99\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n" - ] - } - ], - "source": [ - "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL,\n", - " normalization=normalization)\n", - "pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridEx = Extractor.gridex(predictor, grid, threshold=threshold, normalization=normalization)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "evaluate('GridEx', gridEx, true, predicted)\n", - "print('GridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ] + } }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, + "outputs": [], + "source": [], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 0.00, 72 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 0.00, 4 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridREx performance (86 rules):\n", - "MAE = 0.00\n", - "MAE fidelity = 0.00\n", - "R2 = 0.99\n", - "R2 fidelity = 0.99\n", - "\n", - "GridREx extracted rules:\n", - "\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.33].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.00, 0.33], Y in [0.33, 0.66].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.66, 0.99].\n", - "z(X, Y, 0.48) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.33].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.33, 0.66], Y in [0.33, 0.66].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.66, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.33].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.33, 0.66].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.66, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.99].\n", - "z(X, Y, 0.35) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.20, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.37) :-\n", - " X in [0.40, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.60, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.56) :-\n", - " X in [0.00, 0.10], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.10, 0.20], Y in [0.00, 0.99].\n", - "z(X, Y, 0.54) :-\n", - " X in [0.20, 0.30], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.30, 0.40], Y in [0.00, 0.99].\n", - "z(X, Y, 0.55) :-\n", - " X in [0.40, 0.50], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.50, 0.60], Y in [0.00, 0.99].\n", - "z(X, Y, 0.15) :-\n", - " X in [0.60, 0.70], Y in [0.00, 0.99].\n", - "z(X, Y, 0.13) :-\n", - " X in [0.70, 0.80], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.80, 0.89], Y in [0.00, 0.99].\n", - "z(X, Y, 0.14) :-\n", - " X in [0.89, 0.99], Y in [0.00, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.33], Y in [0.00, 0.49].\n", - "z(X, Y, 0.4) :-\n", - " X in [0.00, 0.33], Y in [0.49, 0.99].\n", - "z(X, Y, 0.52) :-\n", - " X in [0.33, 0.66], Y in [0.00, 0.49].\n", - "z(X, Y, 0.17) :-\n", - " X in [0.33, 0.66], Y in [0.49, 0.99].\n", - "z(X, Y, 0.29) :-\n", - " X in [0.66, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.66, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, 0.7) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49].\n", - "z(X, Y, 0.39) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99].\n", - "z(X, Y, 0.3) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49].\n", - "z(X, Y, 0.0) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99].\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.50], Y in [0.00, 0.49], Z is 0.7.\n", - "z(X, Y, Z) :-\n", - " X in [0.00, 0.50], Y in [0.49, 0.99], Z is 0.40 - 0.07 * X + 0.00 * Y.\n", - "z(X, Y, Z) :-\n", - " X in [0.50, 0.99], Y in [0.00, 0.49], Z is 0.3.\n", - "z(X, Y, Z) :-\n", - " X in [0.50, 0.99], Y in [0.49, 0.99], Z is 0.0.\n" - ] - } - ], - "source": [ - "#pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - "# max_depth=2, patience=1, algorithm=PEDRO.Algorithm.GRIDREX, objective=Objective.MODEL)\n", - "#pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridREx = Extractor.gridrex(predictor, grid, threshold=threshold, normalization=normalization)\n", - "theory_from_gridREx = gridREx.extract(train)\n", - "evaluate('GridREx', gridREx, true, predicted)\n", - "print('GridREx extracted rules:\\n\\n' + pretty_theory(theory_from_gridREx))" - ] + } }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, + "outputs": [], + "source": [], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (4 rules):\n", - "MAE = 0.00\n", - "MAE fidelity = 0.00\n", - "R2 = 1.00\n", - "R2 fidelity = 1.00\n", - "\n", - "CART extracted rules:\n", - "\n", - "z(X, Y, 0.3) :-\n", - " X > 0.50, Y =< 0.49.\n", - "z(X, Y, 0.0) :-\n", - " X > 0.50, Y > 0.49.\n", - "z(X, Y, 0.7) :-\n", - " Y =< 0.50.\n", - "z(X, Y, 0.4).\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, max_depth=4, max_leaves=4, simplify=True, normalization=normalization)\n", - "theory_from_cart = cart.extract(train)\n", - "evaluate('CART', cart, true, predicted)\n", - "print('CART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ] + } } ], "metadata": { diff --git a/demo/README.md b/demo/README.md deleted file mode 100644 index a67d14ac..00000000 --- a/demo/README.md +++ /dev/null @@ -1,40 +0,0 @@ -## About Notebooks files in this directory - -Notebook files (`.ipynb`) in this directory contain demos of PSyKE. - -These are NOT meant to be executed directly, but rather via some container started from the [PSyKE Image](https://hub.docker.com/r/pikalab/psyke) on DockerHub. - -To do so, please ensure -- [Docker](https://docs.docker.com/engine/install/) is installed on your system -- the Docker service is up and running - -and then follow these steps: - -1. Choose a target version of PSyKE, say `X.Y.Z` - - cf. [DockerHub image tags](https://hub.docker.com/r/pikalab/psyke/tags) - - cf. [PyPi releases history](https://pypi.org/project/psyke/#history) - - cf. [GitHub releases](https://github.com/psykei/psyke-python/releases) - -2. Pull the corresponding image: - ```bash - docker pull pikalab/psyke:X.Y.Z - ``` - -2. Run the image into a container: - ```bash - docker run --rm -it -p 8888:8888 --name psyke pikalab/psyke:X.Y.Z - ``` - -3. Some logs such as the following ones should eventually be printed - ``` - To access the notebook, open this file in a browser: - file:///root/.local/share/jupyter/runtime/nbserver-7-open.html - Or copy and paste one of these URLs: - http://66fa9b93bbe7:8888/?token= - or http://127.0.0.1:8888/?token= - ``` - -4. Open your browser and browse to `http://localhost:8888/?token=` - - use the token from the logs above, if requested by the Web page - -5. Enjoy PSyKE-powered notebook! diff --git a/demo/demo.py b/demo/demo.py deleted file mode 100644 index c4d2d124..00000000 --- a/demo/demo.py +++ /dev/null @@ -1,27 +0,0 @@ -from sklearn.datasets import load_iris -from sklearn.model_selection import train_test_split -from sklearn.neighbors import KNeighborsClassifier -import pandas as pd -from psyke import Extractor -from psyke.utils.dataframe import get_discrete_features_equal_frequency, get_discrete_dataset -from psyke.utils.logic import pretty_theory - -x, y = load_iris(return_X_y=True, as_frame=True) -x.columns = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'] -iris_features = get_discrete_features_equal_frequency(x, bins=3, output=False) -x = get_discrete_dataset(x, iris_features) -y = pd.DataFrame(y).replace({"target": {0: 'setosa', 1: 'virginica', 2: 'versicolor'}}) -dataset = x.join(y) -dataset.columns = [*dataset.columns[:-1], 'iris'] -train, test = train_test_split(dataset, test_size=0.5, random_state=0) - -predictor = KNeighborsClassifier(n_neighbors=7) -predictor.fit(train.iloc[:, :-1], train.iloc[:, -1]) - -real = Extractor.real(predictor, iris_features) -theory_from_real = real.extract(train) -print('REAL extracted rules:\n' + pretty_theory(theory_from_real)) - -trepan = Extractor.trepan(predictor, iris_features) -theory_from_trepan = trepan.extract(train) -print('\nTrepan extracted rules:\n' + pretty_theory(theory_from_trepan)) From c6d60eae62776d8da0755e55ba9c9a6b19997f67 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 18 Jun 2023 23:27:54 +0200 Subject: [PATCH 52/67] wip --- demo/DemoRegressionScaled.ipynb | 49 +++++---------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 28f7f265..6a834e0e 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -56,64 +56,29 @@ " gridREx.extract(train)\n", " return gridREx.brute_predict(test) * s + m, gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", - "def cart(model, train, test, normalization, s, m):\n", + "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", " CART.extract(train)\n", - " return CART.predict(test) * s + m, CART.n_rules, 0\n", + " return CART.predict(test), CART.n_rules, 0\n", "\n", - "def cosmik(model, train, test, normalization, s, m):\n", + "def cosmik(model, train, test, normalization):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", " output=Target.CONSTANT, normalization=normalization)\n", " COSMiK.extract(train)\n", - " return COSMiK.brute_predict(test) * s + m, COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", + " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization, s, m):\n", + "def creepy(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", " output=Target.CONSTANT, normalization=normalization)\n", " CReEPy.extract(train)\n", - " return CReEPy.brute_predict(test) * s + m, CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" + " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } -<<<<<<< HEAD - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ITER performance (4 rules):\n", - "MAE = 0.55\n", - "MAE fidelity = 0.55\n", - "R2 = -6.34\n", - "R2 fidelity = -6.34\n", - "\n", - "ITER extracted rules:\n", - "\n", - "z(X, Y, 1.11) :-\n", - " X in [-1.70, -0.00], Y in [-1.69, 0.22].\n", - "z(X, Y, 0.19) :-\n", - " X in [-1.70, -0.00], Y in [0.22, 1.75].\n", - "z(X, Y, -0.11) :-\n", - " X in [-0.00, 1.75], Y in [-1.69, 0.22].\n", - "z(X, Y, -1.29) :-\n", - " X in [-0.00, 1.75], Y in [0.22, 1.75].\n" - ] - } - ], - "source": [ - "it = Extractor.iter(predictor, min_update=1.0 / 10, n_points=1, max_iterations=600,\n", - " min_examples=100, threshold=.4, normalization=normalization)\n", - "theory_from_iter = it.extract(train)\n", - "evaluate('ITER', it, true, predicted)\n", - "print('ITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ] -======= } ->>>>>>> wip }, { "cell_type": "code", @@ -177,7 +142,7 @@ " print(testB, name, end='\\r')\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", - " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", " missed[name].append(miss)" From cb44294e273b98c3601ae79a9ac2d17505688b54 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:00:18 +0200 Subject: [PATCH 53/67] wip --- demo/DemoRegressionScaled.ipynb | 61 ++++++++++++--------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 6a834e0e..79d9abc1 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -49,12 +49,12 @@ "execution_count": 3, "outputs": [], "source": [ - "def gridrex(model, train, test, normalization, s, m):\n", + "def gridrex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", - " return gridREx.brute_predict(test) * s + m, gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", + " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", @@ -63,13 +63,13 @@ "\n", "def cosmik(model, train, test, normalization):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", - " output=Target.CONSTANT, normalization=normalization)\n", + " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", "def creepy(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", - " output=Target.CONSTANT, normalization=normalization)\n", + " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" ], @@ -88,23 +88,29 @@ "name": "stdout", "output_type": "stream", "text": [ - "2491 CART\rEx2491 CReEPy\r" + "2491 CART\rEx2491 COSMiK\r" ] }, { - "ename": "TypeError", - "evalue": "'function' object is not subscriptable", + "ename": "KeyboardInterrupt", + "evalue": "", "output_type": "error", "traceback": [ "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/2296165336.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_28556/1333199017.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 20\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n\u001B[0;32m 21\u001B[0m output=Target.CONSTANT, normalization=normalization)\n\u001B[1;32m---> 22\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 23\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\creepy\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdimension\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdimension\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minf\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 39\u001B[1;33m \u001B[0mtheory\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 40\u001B[0m \u001B[0mlast_clause\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclauses\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 41\u001B[0m \u001B[0mtheory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mretract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mlast_clause\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_create_theory\u001B[1;34m(self, dataframe, sort)\u001B[0m\n\u001B[0;32m 118\u001B[0m \u001B[0mvariables\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mto_var\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 119\u001B[0m head = HyperCubeExtractor._create_head(dataframe, list(variables.values()),\n\u001B[1;32m--> 120\u001B[1;33m self.unscale(cube.output, dataframe.columns[-1]))\n\u001B[0m\u001B[0;32m 121\u001B[0m \u001B[0mbody\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbody\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mvariables\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_ignore_dimensions\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0munscale\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[0mnew_theory\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0massertZ\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mclause\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mhead\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mbody\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36munscale\u001B[1;34m(self, values, name)\u001B[0m\n\u001B[0;32m 76\u001B[0m value * self.normalization[name][1] + self.normalization[name][0] for value in values]\n\u001B[0;32m 77\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 78\u001B[1;33m \u001B[0mvalues\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mvalues\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 79\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mvalues\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 80\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mTypeError\u001B[0m: 'function' object is not subscriptable" + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/2919256357.py\u001B[0m in \u001B[0;36mcosmik\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 15\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[0;32m 16\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 17\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 18\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 19\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mcreepy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mstr\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;34m'corner'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 35\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 36\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " ] } ], @@ -138,7 +144,7 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(['GridREx', 'CART', 'CReEPy'], [gridrex, cart, creepy]):\n", + " for name, fun in zip(['GridREx', 'CART', 'COSMiK', 'CReEPy'], [gridrex, cart, cosmik, creepy]):\n", " print(testB, name, end='\\r')\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", @@ -182,29 +188,6 @@ } } }, - { - "cell_type": "code", - "execution_count": 6, - "outputs": [ - { - "data": { - "text/plain": "{'Bmin': (2.909256272401434, 0.9536815020973909),\n 'Bmedian': (5.282275985663082, 1.4553994817336167),\n 'Bmax': (10.302267025089607, 4.267392124190232),\n 'Btrend': (0.0003254668720864083, 0.05779357910523391),\n 'Bhalf1': (2.92125578628164e-05, 0.10419543281233311),\n 'Bhalf2': (0.0005042227155979311, 0.10530984379227255),\n 'GCRmin': (-3.524847659251415, 1.5620135919428861),\n 'GCRmedian': (-0.013669578853476324, 1.6346771275509167),\n 'GCRmax': (3.732416265082763, 2.1377962993128765),\n 'GCRtrend': (-2.9804500565074855e-05, 0.019833671296497202),\n 'GCRhalf1': (0.0002589125691562307, 0.03025486059161083),\n 'GCRhalf2': (-0.00023048455270716005, 0.030366014637694866),\n 'V': (453.97293906810035, 108.90956420129501)}" - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "normalization" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, { "cell_type": "code", "execution_count": null, From 0fbb22ed3e6676eda36226970ec6f8b398d5504f Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 00:25:37 +0200 Subject: [PATCH 54/67] wip --- demo/DemoRegressionScaled.ipynb | 41 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 79d9abc1..d418bba5 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -82,13 +82,13 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2491 CART\rEx2491 COSMiK\r" + "2492 GridREx\r491 COSMiK2492" ] }, { @@ -98,18 +98,27 @@ "traceback": [ "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_20280/2919256357.py\u001B[0m in \u001B[0;36mcosmik\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 15\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[0;32m 16\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 17\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCOSMiK\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 18\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 19\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mcreepy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36mbrute_predict\u001B[1;34m(self, dataframe, criterion, n)\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcriterion\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mstr\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;34m'corner'\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m2\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 35\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 36\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 37\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m_predict\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 29\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0midx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 31\u001B[1;33m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0midx\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 32\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/2919256357.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 3\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n\u001B[0;32m 4\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m----> 5\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 6\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0msurrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[0msurrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minit_diversity\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m2\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mthreshold\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0msurrounding\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_iterate\u001B[1;34m(self, surrounding, dataframe)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfake\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mto_split\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mto_split\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_merge\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfake\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[0mnext_iteration\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mto_split\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[0mprev\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mnext_iteration\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_merge\u001B[1;34m(self, to_split, dataframe)\u001B[0m\n\u001B[0;32m 105\u001B[0m \u001B[1;31m# TODO: refactor this. A while true with a break is as ugly as hunger.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 106\u001B[0m \u001B[1;32mwhile\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 107\u001B[1;33m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0m\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 109\u001B[0m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 107\u001B[0m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 109\u001B[1;33m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n\u001B[0m\u001B[0;32m 110\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_merge\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m==\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 111\u001B[0m \u001B[1;32mbreak\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_evaluate_merge\u001B[1;34m(self, not_in_cache, dataframe, feature, cube, other_cube, merge_cache)\u001B[0m\n\u001B[0;32m 94\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mor\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 95\u001B[0m \u001B[0mmerged_cube\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmerge_along_dimension\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfeature\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 96\u001B[1;33m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 97\u001B[0m \u001B[0mmerge_cache\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 98\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCLASSIFICATION\u001B[0m \u001B[1;32melse\u001B[0m\u001B[0;31m \u001B[0m\u001B[0;31m\\\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\hypercube.py\u001B[0m in \u001B[0;36mupdate\u001B[1;34m(self, dataset, predictor)\u001B[0m\n\u001B[0;32m 362\u001B[0m \u001B[0mfiltered\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfilter_dataframe\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataset\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 363\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m \u001B[1;33m>\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 364\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 365\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 366\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_diversity\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mabs\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m-\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmean\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", "\u001B[1;31mKeyboardInterrupt\u001B[0m: " ] } @@ -128,7 +137,7 @@ "for testB in TESTB:\n", " rules['BR'].append(testB)\n", " missed['BR'].append(testB)\n", - " print(testB, end='\\r')\n", + " print(testB)\n", "\n", " data = pd.read_csv(f'data/halffuzzycoefs2B.csv', parse_dates=[0], index_col=0)\n", " train, test = getTrainTest(data, testB)\n", @@ -145,7 +154,7 @@ " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", " for name, fun in zip(['GridREx', 'CART', 'COSMiK', 'CReEPy'], [gridrex, cart, cosmik, creepy]):\n", - " print(testB, name, end='\\r')\n", + " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", From c331cff021c7ca3c9215978037dd8a86fa016e24 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:13:35 +0200 Subject: [PATCH 55/67] wip --- demo/DemoRegressionScaled.ipynb | 207 +++++++++++++++++++++++++------- 1 file changed, 161 insertions(+), 46 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index d418bba5..ddf14ec3 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -49,25 +49,32 @@ "execution_count": 3, "outputs": [], "source": [ - "def gridrex(model, train, test, normalization):\n", + "def gridex(model, train, test, normalization, s, m):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", - " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n", + " gridEx = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3), (0.75, 4)])),\n", + " threshold=5, min_examples=1, normalization=normalization)\n", + " gridEx.extract(train)\n", + " return gridEx.brute_predict(test), gridEx.n_rules, sum([p is None for p in gridEx.predict(test)])\n", + " \n", + "def gridrex(model, train, test, normalization, s, m):\n", + " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", + " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", - "def cart(model, train, test, normalization):\n", + "def cart(model, train, test, normalization, s, m):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", " CART.extract(train)\n", - " return CART.predict(test), CART.n_rules, 0\n", + " return CART.predict(test) * s + m, CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", - "def cosmik(model, train, test, normalization):\n", + "def cosmik(model, train, test, normalization, s, m):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", - " output=Target.REGRESSION, normalization=normalization)\n", + " output=Target.CONSTANT, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization):\n", + "def creepy(model, train, test, normalization, s, m):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", @@ -88,38 +95,96 @@ "name": "stdout", "output_type": "stream", "text": [ - "2492 GridREx\r491 COSMiK2492" - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/637015750.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_34008/2919256357.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 3\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3)])),\n\u001B[0;32m 4\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m----> 5\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 6\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 7\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 377\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mjoin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 378\u001B[0m \u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 379\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 380\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 381\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_extract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0msurrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[0msurrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0minit_diversity\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m2\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mthreshold\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0msurrounding\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_theory\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_iterate\u001B[1;34m(self, surrounding, dataframe)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfake\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mto_split\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mto_split\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_merge\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfake\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[0mnext_iteration\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mto_split\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[0mprev\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mnext_iteration\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcopy\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_merge\u001B[1;34m(self, to_split, dataframe)\u001B[0m\n\u001B[0;32m 105\u001B[0m \u001B[1;31m# TODO: refactor this. A while true with a break is as ugly as hunger.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 106\u001B[0m \u001B[1;32mwhile\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 107\u001B[1;33m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0m\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 109\u001B[0m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 107\u001B[0m to_merge = [([cube, other_cube], merge_cache[(cube, other_cube)]) for cube, other_cube, feature in\n\u001B[0;32m 108\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_find_couples\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_split\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0madjacent_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mif\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 109\u001B[1;33m self._evaluate_merge(not_in_cache, dataframe, feature, cube, other_cube, merge_cache)]\n\u001B[0m\u001B[0;32m 110\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mto_merge\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m==\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 111\u001B[0m \u001B[1;32mbreak\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m_evaluate_merge\u001B[1;34m(self, not_in_cache, dataframe, feature, cube, other_cube, merge_cache)\u001B[0m\n\u001B[0;32m 94\u001B[0m \u001B[1;32mif\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;32mor\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mnot_in_cache\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 95\u001B[0m \u001B[0mmerged_cube\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmerge_along_dimension\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mother_cube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfeature\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 96\u001B[1;33m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 97\u001B[0m \u001B[0mmerge_cache\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mcube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmerged_cube\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 98\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mcube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mother_cube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0moutput\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCLASSIFICATION\u001B[0m \u001B[1;32melse\u001B[0m\u001B[0;31m \u001B[0m\u001B[0;31m\\\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\hypercube.py\u001B[0m in \u001B[0;36mupdate\u001B[1;34m(self, dataset, predictor)\u001B[0m\n\u001B[0;32m 362\u001B[0m \u001B[0mfiltered\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfilter_dataframe\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataset\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 363\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m \u001B[1;33m>\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 364\u001B[1;33m \u001B[0mpredictions\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 365\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 366\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_diversity\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0mabs\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfiltered\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m-\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmean\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + "2491\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2492\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2493\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2494\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2495\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2496\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2497\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2498\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2499\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2500\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2501\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2502\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2503\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2504\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2505\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2506\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2507\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2508\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n" ] } ], @@ -157,7 +222,7 @@ " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", - " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", " missed[name].append(miss)" @@ -171,10 +236,10 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "outputs": [], "source": [ - "pd.DataFrame(predicted)" + "pd.DataFrame(predicted).to_csv(\"pred.csv\")" ], "metadata": { "collapsed": false, @@ -185,10 +250,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "outputs": [], "source": [ - "pd.DataFrame(rules)" + "pd.DataFrame(rules).to_csv('rules.csv')\n", + "pd.DataFrame(missed).to_csv('missed.csv')" ], "metadata": { "collapsed": false, @@ -199,9 +265,58 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "outputs": [], - "source": [], + "source": [ + "p = pd.DataFrame(predicted)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 22, + "outputs": [ + { + "data": { + "text/plain": "492505987326.38666" + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(p.COSMiK - p.model).mean()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 23, + "outputs": [ + { + "data": { + "text/plain": " index V model GridREx \\\ncount 11664 11664.000000 11664.000000 1.166400e+04 \nmean 2016-11-01 23:30:00 454.232082 451.424130 9.016708e+09 \nmin 2016-03-04 00:00:00 263.000000 329.304591 -5.506639e+12 \n25% 2016-07-03 11:45:00 368.000000 392.160427 -5.776123e-01 \n50% 2016-11-01 23:30:00 431.000000 442.197565 -1.053650e-01 \n75% 2017-03-03 11:15:00 525.000000 507.905235 5.485643e-01 \nmax 2017-07-02 23:00:00 761.000000 639.798655 5.516910e+12 \nstd NaN 107.353337 70.294346 2.476474e+11 \n\n CART COSMiK CReEPy \ncount 11664.000000 1.166400e+04 11664.000000 \nmean 0.012740 2.897577e+11 455.121051 \nmin -0.890021 -3.547506e+13 284.408661 \n25% -0.757964 3.301165e+02 395.736008 \n50% -0.006947 4.555268e+02 448.432503 \n75% 0.810958 6.599621e+02 510.909673 \nmax 1.196391 1.997025e+14 716.249401 \nstd 0.668172 8.476597e+12 76.059168 ", + "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
indexVmodelGridRExCARTCOSMiKCReEPy
count1166411664.00000011664.0000001.166400e+0411664.0000001.166400e+0411664.000000
mean2016-11-01 23:30:00454.232082451.4241309.016708e+090.0127402.897577e+11455.121051
min2016-03-04 00:00:00263.000000329.304591-5.506639e+12-0.890021-3.547506e+13284.408661
25%2016-07-03 11:45:00368.000000392.160427-5.776123e-01-0.7579643.301165e+02395.736008
50%2016-11-01 23:30:00431.000000442.197565-1.053650e-01-0.0069474.555268e+02448.432503
75%2017-03-03 11:15:00525.000000507.9052355.485643e-010.8109586.599621e+02510.909673
max2017-07-02 23:00:00761.000000639.7986555.516910e+121.1963911.997025e+14716.249401
stdNaN107.35333770.2943462.476474e+110.6681728.476597e+1276.059168
\n
" + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p.describe()" + ], "metadata": { "collapsed": false, "pycharm": { From 19cc8b6afed05aafbeb7d4501daf2480d28c37fb Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 01:47:33 +0200 Subject: [PATCH 56/67] wip --- demo/DemoRegressionScaled.ipynb | 149 +++++++------------------------- 1 file changed, 29 insertions(+), 120 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index ddf14ec3..69677604 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -96,108 +96,35 @@ "output_type": "stream", "text": [ "2491\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2492\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2493\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2494\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2495\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2496\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2497\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2498\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2499\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2500\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2501\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2502\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2503\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2504\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2505\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2506\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2507\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2508\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n" + "GridREx\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'predict'", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mAttributeError\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/1487439369.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/808977561.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 10\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n\u001B[0;32m 11\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m---> 12\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 13\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 14\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 375\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 376\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 377\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 378\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mAttributeError\u001B[0m: 'NoneType' object has no attribute 'predict'" ] } ], "source": [ "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", "\n", + "extractors = ['GridEx', 'GridREx', 'CART', 'COSMiK', 'CReEPy']\n", + "\n", "TESTB = [i for i in range(2491, 2509)]\n", "\n", - "predicted = {'index': [], 'V': [], 'model': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "predicted = {name: [] for name in ['index', 'V', 'model'] + extractors}\n", "\n", - "rules = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "rules = {name: [] for name in ['BR'] + extractors}\n", "\n", - "missed = {'BR': [], 'GridREx': [], 'CART': [], 'COSMiK': [], 'CReEPy': []}\n", + "missed = {name: [] for name in ['BR'] + extractors}\n", "\n", "for testB in TESTB:\n", " rules['BR'].append(testB)\n", @@ -218,14 +145,15 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(['GridREx', 'CART', 'COSMiK', 'CReEPy'], [gridrex, cart, cosmik, creepy]):\n", + " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)" + " missed[name].append(miss)\n", + " break" ], "metadata": { "collapsed": false, @@ -236,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "outputs": [], "source": [ "pd.DataFrame(predicted).to_csv(\"pred.csv\")" @@ -250,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "outputs": [], "source": [ "pd.DataFrame(rules).to_csv('rules.csv')\n", @@ -265,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "outputs": [], "source": [ "p = pd.DataFrame(predicted)" @@ -279,17 +207,8 @@ }, { "cell_type": "code", - "execution_count": 22, - "outputs": [ - { - "data": { - "text/plain": "492505987326.38666" - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "abs(p.COSMiK - p.model).mean()" ], @@ -302,18 +221,8 @@ }, { "cell_type": "code", - "execution_count": 23, - "outputs": [ - { - "data": { - "text/plain": " index V model GridREx \\\ncount 11664 11664.000000 11664.000000 1.166400e+04 \nmean 2016-11-01 23:30:00 454.232082 451.424130 9.016708e+09 \nmin 2016-03-04 00:00:00 263.000000 329.304591 -5.506639e+12 \n25% 2016-07-03 11:45:00 368.000000 392.160427 -5.776123e-01 \n50% 2016-11-01 23:30:00 431.000000 442.197565 -1.053650e-01 \n75% 2017-03-03 11:15:00 525.000000 507.905235 5.485643e-01 \nmax 2017-07-02 23:00:00 761.000000 639.798655 5.516910e+12 \nstd NaN 107.353337 70.294346 2.476474e+11 \n\n CART COSMiK CReEPy \ncount 11664.000000 1.166400e+04 11664.000000 \nmean 0.012740 2.897577e+11 455.121051 \nmin -0.890021 -3.547506e+13 284.408661 \n25% -0.757964 3.301165e+02 395.736008 \n50% -0.006947 4.555268e+02 448.432503 \n75% 0.810958 6.599621e+02 510.909673 \nmax 1.196391 1.997025e+14 716.249401 \nstd 0.668172 8.476597e+12 76.059168 ", - "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
indexVmodelGridRExCARTCOSMiKCReEPy
count1166411664.00000011664.0000001.166400e+0411664.0000001.166400e+0411664.000000
mean2016-11-01 23:30:00454.232082451.4241309.016708e+090.0127402.897577e+11455.121051
min2016-03-04 00:00:00263.000000329.304591-5.506639e+12-0.890021-3.547506e+13284.408661
25%2016-07-03 11:45:00368.000000392.160427-5.776123e-01-0.7579643.301165e+02395.736008
50%2016-11-01 23:30:00431.000000442.197565-1.053650e-01-0.0069474.555268e+02448.432503
75%2017-03-03 11:15:00525.000000507.9052355.485643e-010.8109586.599621e+02510.909673
max2017-07-02 23:00:00761.000000639.7986555.516910e+121.1963911.997025e+14716.249401
stdNaN107.35333770.2943462.476474e+110.6681728.476597e+1276.059168
\n
" - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "p.describe()" ], From 32e6fccf903ec8c7424397edd7872ba11f0e3d11 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:29:40 +0200 Subject: [PATCH 57/67] wip --- demo/DemoRegressionScaled.ipynb | 154 +++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 23 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 69677604..3c44844c 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -49,32 +49,32 @@ "execution_count": 3, "outputs": [], "source": [ - "def gridex(model, train, test, normalization, s, m):\n", + "def gridex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridEx = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3), (0.75, 4)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridEx.extract(train)\n", " return gridEx.brute_predict(test), gridEx.n_rules, sum([p is None for p in gridEx.predict(test)])\n", " \n", - "def gridrex(model, train, test, normalization, s, m):\n", + "def gridrex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n", " threshold=5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", - "def cart(model, train, test, normalization, s, m):\n", + "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", " CART.extract(train)\n", - " return CART.predict(test) * s + m, CART.n_rules, sum([p is None for p in CART.predict(test)])\n", + " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", - "def cosmik(model, train, test, normalization, s, m):\n", + "def cosmik(model, train, test, normalization):\n", " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", " output=Target.CONSTANT, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization, s, m):\n", + "def creepy(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", @@ -95,21 +95,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2491\n", - "GridREx\n" - ] - }, - { - "ename": "AttributeError", - "evalue": "'NoneType' object has no attribute 'predict'", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mAttributeError\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/1487439369.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_43480/808977561.py\u001B[0m in \u001B[0;36mgridrex\u001B[1;34m(model, train, test, normalization, s, m)\u001B[0m\n\u001B[0;32m 10\u001B[0m gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n\u001B[0;32m 11\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[1;32m---> 12\u001B[1;33m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 13\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mgridREx\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 14\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 375\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 376\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mdict\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mstr\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mbool\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mTheory\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 377\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 378\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mAttributeError\u001B[0m: 'NoneType' object has no attribute 'predict'" + "2491\n" ] } ], @@ -144,12 +130,12 @@ " model = KNN(200, weights='distance', p=1).fit(scaledTrain.iloc[:, :-1], scaledTrain.iloc[:, -1])\n", " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", - "\n", + " break\n", " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", - " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization, s, m)\n", + " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", " missed[name].append(miss)\n", @@ -162,6 +148,128 @@ } } }, + { + "cell_type": "code", + "execution_count": 13, + "outputs": [ + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_11724/3287760623.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mCART\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcart\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 2\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 3\u001B[1;33m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, dataframe, mapping)\u001B[0m\n\u001B[0;32m 57\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;32mreturn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0ma\u001B[0m \u001B[0mlist\u001B[0m \u001B[0mof\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 58\u001B[0m \"\"\"\n\u001B[1;32m---> 59\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__convert\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 60\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + ] + } + ], + "source": [ + "CART = Extractor.cart(model, normalization=normalization)\n", + "CART.extract(scaledTrain)\n", + "p = CART.predict(scaledTest)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [ + { + "data": { + "text/plain": "(648, 648)" + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.array(p).shape" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [ + { + "data": { + "text/plain": "(648,)" + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.predict(scaledTest).shape" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "data": { + "text/plain": " index V model GridEx GridREx \\\n0 2016-03-04 00:00:00 410.0 518.750997 0.0 -0.381030 \n1 2016-03-04 01:00:00 400.0 522.352090 0.0 -0.407892 \n2 2016-03-04 02:00:00 395.0 525.129143 0.0 -0.433331 \n3 2016-03-04 03:00:00 408.0 528.767307 0.0 -0.461158 \n4 2016-03-04 04:00:00 406.0 528.091871 0.0 -0.477063 \n.. ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 0.0 -0.294504 \n644 2016-03-30 20:00:00 501.0 489.614882 0.0 -0.278289 \n645 2016-03-30 21:00:00 518.0 490.609540 0.0 -0.267642 \n646 2016-03-30 22:00:00 510.0 491.321955 0.0 -0.251590 \n647 2016-03-30 23:00:00 511.0 491.970557 0.0 -0.229831 \n\n CART \\\n0 [441.86581920903956, 441.86581920903956, 441.8... \n1 [441.86581920903956, 441.86581920903956, 441.8... \n2 [441.86581920903956, 441.86581920903956, 441.8... \n3 [441.86581920903956, 441.86581920903956, 441.8... \n4 [441.86581920903956, 441.86581920903956, 441.8... \n.. ... \n643 [441.86581920903956, 441.86581920903956, 441.8... \n644 [441.86581920903956, 441.86581920903956, 441.8... \n645 [441.86581920903956, 441.86581920903956, 441.8... \n646 [441.86581920903956, 441.86581920903956, 441.8... \n647 [441.86581920903956, 441.86581920903956, 441.8... \n\n COSMiK \\\n0 [340.3877551020408, 340.3877551020408, 340.387... \n1 [340.3877551020408, 340.3877551020408, 340.387... \n2 [340.3877551020408, 340.3877551020408, 340.387... \n3 [340.3877551020408, 340.3877551020408, 340.387... \n4 [340.3877551020408, 340.3877551020408, 340.387... \n.. ... \n643 [340.3877551020408, 340.3877551020408, 340.387... \n644 [340.3877551020408, 340.3877551020408, 340.387... \n645 [340.3877551020408, 340.3877551020408, 340.387... \n646 [340.3877551020408, 340.3877551020408, 340.387... \n647 [340.3877551020408, 340.3877551020408, 340.387... \n\n CReEPy \n0 [464.19181456754364, 462.0928005366921, 459.64... \n1 [464.19181456754364, 462.0928005366921, 459.64... \n2 [464.19181456754364, 462.0928005366921, 459.64... \n3 [464.19181456754364, 462.0928005366921, 459.64... \n4 [464.19181456754364, 462.0928005366921, 459.64... \n.. ... \n643 [464.19181456754364, 462.0928005366921, 459.64... \n644 [464.19181456754364, 462.0928005366921, 459.64... \n645 [464.19181456754364, 462.0928005366921, 459.64... \n646 [464.19181456754364, 462.0928005366921, 459.64... \n647 [464.19181456754364, 462.0928005366921, 459.64... \n\n[648 rows x 8 columns]", + "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.7509970.0-0.381030[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
12016-03-04 01:00:00400.0522.3520900.0-0.407892[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
22016-03-04 02:00:00395.0525.1291430.0-0.433331[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
32016-03-04 03:00:00408.0528.7673070.0-0.461158[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
42016-03-04 04:00:00406.0528.0918710.0-0.477063[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
...........................
6432016-03-30 19:00:00497.0487.5210850.0-0.294504[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6442016-03-30 20:00:00501.0489.6148820.0-0.278289[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6452016-03-30 21:00:00518.0490.6095400.0-0.267642[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6462016-03-30 22:00:00510.0491.3219550.0-0.251590[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6472016-03-30 23:00:00511.0491.9705570.0-0.229831[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
\n

648 rows × 8 columns

\n
" + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.DataFrame(predicted)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "sdsfgd" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": null, From e1a0b92a90bde3ebffa64d2796d0dab05993d558 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 19 Jun 2023 02:37:00 +0200 Subject: [PATCH 58/67] wip --- demo/DemoRegressionScaled.ipynb | 43 +++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 3c44844c..bcf9ca55 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -150,7 +150,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 19, "outputs": [ { "ename": "KeyboardInterrupt", @@ -159,10 +159,11 @@ "traceback": [ "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_11724/3287760623.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mCART\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcart\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 2\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 3\u001B[1;33m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, dataframe, mapping)\u001B[0m\n\u001B[0;32m 57\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;32mreturn\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0ma\u001B[0m \u001B[0mlist\u001B[0m \u001B[0mof\u001B[0m \u001B[0mpredictions\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 58\u001B[0m \"\"\"\n\u001B[1;32m---> 59\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__convert\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmapping\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 60\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36m__convert\u001B[1;34m(self, ys, mapping)\u001B[0m\n\u001B[0;32m 68\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 69\u001B[0m \u001B[0mm\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkeys\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 70\u001B[1;33m \u001B[0mys\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m[\u001B[0m\u001B[0mprediction\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mys\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0ms\u001B[0m \u001B[1;33m+\u001B[0m \u001B[0mm\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mprediction\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 71\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mys\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 72\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_7300/1366764375.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mranked\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mFeatureRanker\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mrankings\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 2\u001B[1;33m CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n\u001B[0m\u001B[0;32m 3\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[0;32m 4\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mgridex\u001B[1;34m(predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 298\u001B[0m \"\"\"\n\u001B[0;32m 299\u001B[0m \u001B[1;32mfrom\u001B[0m \u001B[0mpsyke\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextraction\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mhypercubic\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgridex\u001B[0m \u001B[1;32mimport\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 300\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthreshold\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mseed\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 301\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 302\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 18\u001B[0m def __init__(self, predictor, grid: Grid, min_examples: int, threshold: float, normalization=None,\n\u001B[0;32m 19\u001B[0m seed=get_default_random_seed()):\n\u001B[1;32m---> 20\u001B[1;33m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCONSTANT\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 21\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgrid\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 22\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmin_examples\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", @@ -175,7 +176,9 @@ } ], "source": [ - "CART = Extractor.cart(model, normalization=normalization)\n", + "ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", + "CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n", + " threshold=5, min_examples=1, normalization=normalization)\n", "CART.extract(scaledTrain)\n", "p = CART.predict(scaledTest)" ], @@ -188,13 +191,27 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 17, + "outputs": [], + "source": [ + "CART.normalization" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 15, "outputs": [ { "data": { - "text/plain": "(648, 648)" + "text/plain": "(648,)" }, - "execution_count": 9, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -211,19 +228,19 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 16, "outputs": [ { "data": { - "text/plain": "(648,)" + "text/plain": "array([ 5.94787596e-01, 6.27852580e-01, 6.53351288e-01, 6.86756656e-01,\n 6.80554845e-01, 6.56268596e-01, 6.41509728e-01, 6.23215360e-01,\n 5.06968206e-01, 4.49547691e-01, 4.50112948e-01, 3.89409986e-01,\n 4.17987119e-01, 4.47363530e-01, 4.73589506e-01, 5.27920839e-01,\n 5.65069947e-01, 6.30275078e-01, 6.50014692e-01, 6.25667786e-01,\n 5.32084121e-01, 5.11315913e-01, 4.66818875e-01, 3.79488214e-01,\n 3.49910991e-01, 2.21345403e-01, 1.93290286e-01, 1.29067767e-01,\n 1.29278790e-01, 9.70285114e-02, 1.16471507e-01, 1.20058101e-01,\n 1.10625154e-01, 9.43989669e-02, 4.62561519e-02, 1.54927422e-02,\n -3.67243793e-02, -9.73570606e-02, -8.82136957e-02, -8.59049356e-02,\n -1.88867848e-01, -1.98258070e-01, -2.14678123e-01, -2.25880942e-01,\n -2.73753227e-01, -2.86873033e-01, -3.36773813e-01, -3.41153580e-01,\n -2.72939657e-01, -2.60564106e-01, -2.22866189e-01, -3.16103250e-01,\n -4.57077669e-01, -5.69617624e-01, -4.85024701e-01, -4.38443662e-01,\n -1.81425306e-01, 3.16564715e-02, 2.01502725e-01, 3.30505305e-01,\n 4.34886887e-01, 4.88884946e-01, 4.81334210e-01, 4.72318295e-01,\n 4.49382717e-01, 3.71368779e-01, 3.10463488e-01, 2.27584909e-01,\n 1.96014710e-01, 1.21620524e-01, 8.96840584e-02, 9.66028209e-02,\n 5.56857290e-02, 9.76252433e-02, 1.16906912e-01, 9.32035580e-02,\n 1.61667957e-01, 2.43306894e-01, 4.00764438e-01, 4.08452018e-01,\n 5.04550610e-01, 6.33769305e-01, 7.70897913e-01, 8.66805211e-01,\n 9.64611914e-01, 9.24679976e-01, 9.83779612e-01, 9.57014376e-01,\n 1.18224046e+00, 1.18196180e+00, 1.25001824e+00, 1.26741209e+00,\n 1.31358148e+00, 1.30732589e+00, 1.27459292e+00, 1.28298857e+00,\n 1.30996624e+00, 1.33409461e+00, 1.34064098e+00, 1.33456069e+00,\n 1.26926162e+00, 1.23751719e+00, 1.21728041e+00, 1.18781088e+00,\n 1.07904111e+00, 9.78348868e-01, 8.94449891e-01, 8.02642930e-01,\n 7.67312190e-01, 7.79881638e-01, 7.56459980e-01, 6.39844731e-01,\n 5.59816075e-01, 5.07320578e-01, 3.96137907e-01, 3.08059300e-01,\n 3.49538875e-01, 4.37822132e-01, 5.19863709e-01, 4.83896648e-01,\n 5.49159005e-01, 6.51009546e-01, 6.66199773e-01, 6.75533692e-01,\n 7.14284944e-01, 7.49667837e-01, 7.27343955e-01, 7.50315379e-01,\n 7.35121200e-01, 7.11411018e-01, 6.50371738e-01, 6.57852210e-01,\n 6.88303646e-01, 6.88771323e-01, 6.53846882e-01, 6.39351067e-01,\n 6.42534572e-01, 6.11897413e-01, 5.48695256e-01, 5.36093577e-01,\n 4.68813273e-01, 4.81517348e-01, 4.16924579e-01, 2.27269556e-01,\n 6.95082620e-02, 4.73304329e-02, 4.31446914e-02, 1.51598952e-03,\n -1.63872715e-01, -2.66752323e-01, -3.04611850e-01, -3.09940192e-01,\n -3.80448778e-01, -5.20578055e-01, -5.58929636e-01, -5.66185437e-01,\n -5.50515452e-01, -4.97476048e-01, -4.69612472e-01, -4.37668499e-01,\n -4.08758754e-01, -4.70707072e-01, -5.20063597e-01, -5.77177092e-01,\n -6.33710168e-01, -6.80554694e-01, -7.54318656e-01, -7.98758101e-01,\n -8.44280781e-01, -8.98363509e-01, -8.95358460e-01, -8.86503609e-01,\n -8.85599683e-01, -8.82890386e-01, -8.53614367e-01, -8.31532738e-01,\n -8.33123651e-01, -6.27513729e-01, -1.37548268e-01, 1.58522033e-01,\n 3.43769358e-01, 3.53969327e-01, 3.30450997e-01, 3.16298042e-01,\n 3.07723557e-01, 2.50430376e-01, 1.79257019e-01, 1.48908911e-01,\n 1.94124475e-01, 2.96394183e-01, 3.65626353e-01, 4.03952940e-01,\n 4.89904130e-01, 5.43864115e-01, 5.70022845e-01, 5.78188313e-01,\n 6.02378943e-01, 5.53382870e-01, 5.76370663e-01, 4.53366033e-01,\n 5.46894816e-01, 6.22814657e-01, 6.98452824e-01, 8.43786807e-01,\n 9.03241794e-01, 9.80655929e-01, 1.00069167e+00, 1.03552441e+00,\n 1.05781862e+00, 1.03407151e+00, 1.01060741e+00, 9.95349018e-01,\n 9.83748339e-01, 9.77971820e-01, 9.59232606e-01, 9.75587590e-01,\n 9.86886799e-01, 9.89016229e-01, 1.01371718e+00, 1.02127174e+00,\n 1.01666168e+00, 1.06380858e+00, 1.04014026e+00, 9.68632461e-01,\n 8.50561688e-01, 7.88313619e-01, 8.60332530e-01, 8.39510615e-01,\n 8.19082407e-01, 8.41616540e-01, 8.45556255e-01, 8.78204007e-01,\n 9.01526215e-01, 8.99408318e-01, 9.75302328e-01, 1.07239590e+00,\n 1.12769248e+00, 1.10865204e+00, 1.16262617e+00, 1.19097896e+00,\n 1.18783719e+00, 1.13230601e+00, 1.14472708e+00, 1.16748934e+00,\n 1.15556360e+00, 1.15497319e+00, 1.14661413e+00, 1.12722389e+00,\n 1.11759420e+00, 1.11397318e+00, 1.12588339e+00, 1.10233146e+00,\n 1.06748597e+00, 9.94411455e-01, 9.34246345e-01, 9.26761867e-01,\n 8.79439448e-01, 8.34147761e-01, 7.88468961e-01, 2.63101924e-01,\n 2.58441893e-01, 2.75837911e-01, 3.73833340e-01, 4.06661808e-01,\n 4.50858947e-01, 4.25019531e-01, 3.93777296e-01, 4.25283768e-01,\n 4.50114669e-01, 4.32118982e-01, 4.26723712e-01, 4.25086302e-01,\n 4.12253850e-01, 4.20772144e-01, 3.73951452e-01, 3.84730496e-01,\n 4.15677813e-01, 4.70782896e-01, 5.18673694e-01, 6.28786146e-01,\n 7.11168389e-01, 8.38840673e-01, 9.24391393e-01, 1.00049266e+00,\n 1.02386999e+00, 1.07021416e+00, 1.09681443e+00, 1.14425776e+00,\n 1.35203664e+00, 1.36045510e+00, 1.41445129e+00, 1.42926777e+00,\n 1.48989705e+00, 1.48908458e+00, 1.52517735e+00, 1.55774308e+00,\n 1.53297116e+00, 1.48283249e+00, 1.45336878e+00, 1.43025635e+00,\n 1.42162708e+00, 1.39982454e+00, 1.38697364e+00, 1.40470142e+00,\n 1.41465673e+00, 1.41616546e+00, 1.39234617e+00, 1.39974420e+00,\n 1.37379750e+00, 1.31108897e+00, 1.29610318e+00, 1.26624619e+00,\n 1.21507803e+00, 1.18620871e+00, 1.16431475e+00, 1.08350728e+00,\n 1.04351910e+00, 8.14857616e-01, 9.19351580e-01, 9.92161799e-01,\n 1.05183656e+00, 1.06302057e+00, 1.15394942e+00, 1.26940951e+00,\n 1.28522890e+00, 1.44338788e+00, 1.36122698e+00, 1.22730775e+00,\n 1.26271454e+00, 1.25683022e+00, 1.26641669e+00, 1.35523754e+00,\n 1.35363264e+00, 1.19374746e+00, 8.84222340e-01, 8.45086765e-01,\n 8.26652842e-01, 7.78535822e-01, 7.91430635e-01, 7.51209328e-01,\n 7.16570137e-01, 7.12982315e-01, 6.71535968e-01, 6.59427982e-01,\n 6.61309295e-01, 6.00194599e-01, 5.46063131e-01, 5.06583233e-01,\n 4.60146040e-01, 4.13683133e-01, 3.27972661e-01, 2.84937524e-01,\n 2.45562643e-01, 2.77127619e-01, 2.53966681e-01, 2.33893354e-01,\n 2.36568447e-01, 2.28037916e-01, 2.49038569e-01, 2.64400726e-01,\n 2.94056569e-01, 2.90707304e-01, 2.83857999e-01, 3.01860592e-01,\n 3.14841571e-01, 3.39277627e-01, 3.01466072e-01, 2.93500775e-01,\n 2.87430279e-01, 2.70941481e-01, 3.06142458e-01, 3.36453506e-01,\n 3.49177875e-01, 3.60445946e-01, 3.73710668e-01, 4.04441898e-01,\n 3.94960073e-01, 3.99827257e-01, 3.77291659e-01, 3.30380384e-01,\n 3.37732723e-01, 3.47186442e-01, 3.52084474e-01, 3.77269993e-01,\n 3.77988341e-01, 3.80380920e-01, 3.69822861e-01, 3.58249184e-01,\n 3.64697520e-01, 3.33135345e-01, 3.09091741e-01, 3.80991398e-01,\n 3.61867705e-01, 3.19234148e-01, 3.21624558e-01, 2.17452675e-01,\n 1.92589914e-01, 1.79242718e-01, 1.53009476e-01, 9.77992145e-02,\n 3.23604394e-02, -2.94612149e-02, -1.14045668e-01, -1.55128381e-01,\n -1.57616220e-01, -1.71918305e-01, -1.50894217e-01, -1.67564490e-01,\n -1.88622273e-01, -2.07652581e-01, -2.58115218e-01, -2.93005352e-01,\n -3.38270047e-01, -1.49019196e-01, -3.44280222e-01, -3.28079666e-01,\n -3.26221855e-01, -2.34694433e-01, -2.19842775e-01, -2.43728538e-01,\n -2.38287675e-01, -2.07637015e-01, -2.99053400e-01, -3.51944773e-01,\n -2.98335213e-01, -2.75216185e-01, -2.52291688e-01, -5.92295676e-01,\n -6.29698629e-01, -6.37034248e-01, -6.67193507e-01, -6.61285775e-01,\n -6.59767960e-01, -6.55582862e-01, -6.60398712e-01, -6.48089994e-01,\n -6.30606033e-01, -6.13985068e-01, -5.94299126e-01, -5.51793470e-01,\n -5.21829072e-01, -5.33152444e-01, -5.58533647e-01, -5.30664797e-01,\n -4.96012827e-01, -4.81793417e-01, -4.94722300e-01, -5.05906482e-01,\n -5.84771524e-01, -6.21891793e-01, -6.57652629e-01, -6.74505274e-01,\n -6.77925168e-01, -6.63411854e-01, -6.70761225e-01, -6.71313501e-01,\n -6.78285438e-01, -6.88366243e-01, -6.75084854e-01, -6.86645949e-01,\n -6.86878877e-01, -7.03152004e-01, -7.11550530e-01, -7.06169968e-01,\n -7.06030613e-01, -7.11075855e-01, -6.80750629e-01, -6.72296413e-01,\n -6.65302849e-01, -6.74474185e-01, -6.70617066e-01, -6.70406918e-01,\n -6.31489293e-01, -5.41195959e-01, -5.06100680e-01, -4.73968482e-01,\n -4.80026300e-01, -4.66280906e-01, -4.94750733e-01, -4.66159421e-01,\n -4.53704671e-01, -4.65426691e-01, -4.67830842e-01, -4.48499002e-01,\n -4.37999267e-01, -4.06635710e-01, -4.36565611e-01, -4.87644475e-01,\n -5.10015971e-01, -5.26553795e-01, -5.24534882e-01, -5.17204913e-01,\n -5.18734726e-01, -4.88314619e-01, -4.81448198e-01, -4.49654115e-01,\n -3.59544883e-01, -2.89470852e-01, -2.52844742e-01, -1.92796426e-01,\n 3.61641165e-02, 2.67556715e-01, 2.88468372e-01, 2.69765539e-01,\n 3.03003617e-01, 1.56430282e-01, 8.94392059e-02, 8.35489348e-02,\n 9.53608325e-02, 1.06359033e-01, 4.85854783e-02, 5.95200015e-02,\n 8.92134085e-02, 1.71655377e-01, -1.62529760e-01, -9.18029145e-02,\n -7.00795871e-03, 3.55558744e-02, 7.13713389e-02, 8.51286206e-02,\n 8.85206272e-02, 9.03776601e-02, 7.80484532e-02, 7.76571930e-02,\n 7.06369278e-02, 5.61746134e-02, 7.89197268e-02, 8.51250688e-02,\n 1.11657860e-01, 8.77699810e-02, 1.40152096e-01, 1.41833281e-01,\n 1.40840041e-01, 1.85234816e-01, 1.53057074e-01, 1.61289830e-01,\n 1.60056307e-01, 1.47913238e-01, 1.59117011e-01, 1.24454678e-01,\n 8.40377871e-02, 9.25309859e-02, 8.57329210e-02, 7.57535730e-02,\n 6.16577201e-02, 1.06938161e-01, 1.02192008e-01, 1.71101839e-01,\n 2.19090649e-01, 1.94452383e-01, 1.52915395e-01, 1.42999064e-01,\n 2.34941176e-02, -1.69292889e-02, -4.84760026e-02, -8.84035972e-02,\n -1.06477491e-01, -1.46097303e-01, -1.68977796e-01, -1.69546328e-01,\n -3.06123179e-01, -2.86830814e-01, -3.27339805e-01, -3.04824309e-01,\n -3.08424522e-01, -3.13009099e-01, -3.08204615e-01, -3.30643627e-01,\n -3.47108546e-01, -3.71663036e-01, -3.69800392e-01, -3.81838621e-01,\n -4.11920074e-01, -4.26296448e-01, -4.33323503e-01, -4.32084854e-01,\n -4.26418566e-01, -3.99080526e-01, -3.43601617e-01, -3.32431572e-01,\n -3.17056043e-01, -2.77115852e-01, -2.52202840e-01, -2.14129148e-01,\n -2.00518462e-01, -1.46597547e-01, 2.36017486e-01, 2.72685842e-01,\n 3.48128210e-01, 2.84062462e-01, 2.81911796e-01, 2.71269801e-01,\n 2.17936171e-01, 1.81334194e-01, 1.55982915e-01, 1.36845118e-01,\n 8.53148103e-02, 5.47081435e-02, -1.14402861e-02, -5.94431312e-02,\n -9.09789281e-02, -1.44687840e-01, -1.67931366e-01, -1.76196230e-01,\n -1.84964598e-01, -1.80156999e-01, -1.82591858e-01, -2.04167736e-01,\n -2.17966566e-01, -1.25904259e-01, -1.53099147e-01, -1.62306106e-01,\n -2.16528907e-01, -2.69388887e-01, -3.25151001e-01, -5.16529016e-02,\n 2.34635280e-01, 2.86861522e-01, 2.49274427e-01, 1.65100897e-01,\n 1.31739752e-01, 1.38150405e-01, 1.26449068e-01, 1.62529445e-01,\n 1.71290348e-01, 1.77941660e-01, 1.68343103e-01, 1.21794429e-01,\n 7.66499527e-02, 9.79244195e-02, 1.13147159e-01, 1.19400540e-01,\n 1.31218961e-01, 1.32184095e-01, 1.21069556e-01, 1.14229094e-01,\n 1.29832401e-01, 1.24523142e-01, 1.39992356e-01, 1.42168147e-01,\n 2.48498740e-01, 2.74234518e-01, 3.09358436e-01, 3.08036727e-01,\n 3.27261828e-01, 3.36394705e-01, 3.42936055e-01, 3.48891465e-01])" }, - "execution_count": 11, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "model.predict(scaledTest).shape" + "model.predict(scaledTest)" ], "metadata": { "collapsed": false, From 3f1a20f7d5f96c5a623f38bad77331372bec58a9 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 00:16:08 +0200 Subject: [PATCH 59/67] wip --- demo/DemoRegression.ipynb | 2010 ------------------------------- demo/DemoRegressionScaled.ipynb | 325 +++-- 2 files changed, 204 insertions(+), 2131 deletions(-) delete mode 100644 demo/DemoRegression.ipynb diff --git a/demo/DemoRegression.ipynb b/demo/DemoRegression.ipynb deleted file mode 100644 index 3bef7b2a..00000000 --- a/demo/DemoRegression.ipynb +++ /dev/null @@ -1,2010 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "f52126f3", - "metadata": {}, - "source": [ - "# PSyKE's demo for regression tasks\n", - "\n", - "Some imports." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "6b710e7c", - "metadata": {}, - "outputs": [], - "source": [ - "from psyke import Extractor, Clustering, EvaluableModel\n", - "from psyke.tuning.pedro import PEDRO\n", - "from psyke.tuning import Objective\n", - "from psyke.tuning.crash import CRASH\n", - "from sklearn.tree import DecisionTreeRegressor\n", - "from psyke.utils.logic import pretty_theory\n", - "from psyke.utils.metrics import mae, mse, r2\n", - "from sklearn.model_selection import train_test_split\n", - "from psyke.utils import Target\n", - "import pandas as pd" - ] - }, - { - "cell_type": "markdown", - "id": "d7c90ed2", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "Import a dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "f8e46c49", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = pd.read_csv(\"../test/resources/datasets/df.csv\")\n", - "dataset = dataset[[\"X\", \"Y\", \"Z0\"]].dropna()\n", - "#dataset = pd.read_csv(\"../test/resources/datasets/CCPP.csv\", sep=\";\", decimal=\",\")\n", - "#dataset" - ] - }, - { - "cell_type": "markdown", - "id": "d673b766", - "metadata": {}, - "source": [ - "Split between train and test set in a reproducible way." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "03fc5e2c", - "metadata": {}, - "outputs": [], - "source": [ - "train, test = train_test_split(dataset, test_size=0.85, random_state=10)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "outputs": [], - "source": [ - "#from psyke.tuning.orchid import OrCHiD\n", - "\n", - "#orchid = OrCHiD(dataframe=train, algorithm=OrCHiD.Algorithm.CREAM, output=Target.REGRESSION,\n", - "# max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1, patience=5, max_depth=3)\n", - "#orchid.search()\n", - "#(_, _, depth, threshold) = orchid.get_best()[0]" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "id": "fa6754a0", - "metadata": {}, - "source": [ - "We use as predictor a KNN and we train it." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "bed764ca", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MAE = 0.00\n", - "MSE = 0.00\n", - "R2 = 1.00\n" - ] - } - ], - "source": [ - "#predictor = KNeighborsRegressor(n_neighbors=3).fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "predictor = DecisionTreeRegressor().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "#predictor = LinearRegression().fit(train.iloc[:, :-1], train.iloc[:, -1])\n", - "\n", - "predicted = predictor.predict(test.iloc[:, :-1]).flatten()\n", - "true = test.iloc[:, -1]\n", - "\n", - "print(f'MAE = {mae(true, predicted):.2f}')\n", - "print(f'MSE = {mse(true, predicted):.2f}')\n", - "print(f'R2 = {r2(true, predicted):.2f}')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "We define a function to print the extractors' evaluation" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def print_scores(scores):\n", - " print(f'MAE = {scores[EvaluableModel.RegressionScore.MAE][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.RegressionScore.MAE][1]:.2f} (BB)\\n'\n", - " f'MSE = {scores[EvaluableModel.RegressionScore.MSE][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.RegressionScore.MSE][1]:.2f} (BB)\\n'\n", - " f'R2 = {scores[EvaluableModel.RegressionScore.R2][0]:.2f} (data), '\n", - " f'{scores[EvaluableModel.RegressionScore.R2][1]:.2f} (BB)')\n", - "\n", - "def get_scores(extractor, test, predictor):\n", - " return extractor.score(test, predictor, True, True, task=EvaluableModel.Task.REGRESSION,\n", - " scoring_function=[EvaluableModel.RegressionScore.MAE, EvaluableModel.RegressionScore.MSE,\n", - " EvaluableModel.RegressionScore.R2])" - ] - }, - { - "cell_type": "markdown", - "id": "96835867", - "metadata": {}, - "source": [ - "We create several extractors that use ITER, GridEx and GridREx algorithms to extract prolog rules from the predictor." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "COSMiK performance (4 rules with 87.83% coverage):\n", - "MAE = 0.00 (data), 0.00 (BB)\n", - "MSE = 0.00 (data), 0.00 (BB)\n", - "R2 = 1.00 (data), 1.00 (BB)\n", - "\n", - "COSMiK extracted rules:\n", - "\n", - "'Z0'(X, Y, 1.0) :-\n", - " X in [0.0, 0.48], Y in [0.0, 0.48].\n", - "'Z0'(X, Y, -1.0) :-\n", - " X in [0.65, 1.0], Y in [0.0, 0.44].\n", - "'Z0'(X, Y, -1.0) :-\n", - " X in [0.90, 1.0], Y in [0.75, 1.0].\n", - "'Z0'(X, Y, 0.0) :-\n", - " X in [0.05, 0.83], Y in [0.57, 0.99].\n" - ] - } - ], - "source": [ - "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True)\n", - "theory_from_cosmik = cosmik.extract(train)\n", - "scores, completeness = get_scores(cosmik, test, predictor)\n", - "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "COSMiK performance (4 rules with 89.92% coverage):\n", - "MAE = 0.08 (data), 0.08 (BB)\n", - "MSE = 0.03 (data), 0.03 (BB)\n", - "R2 = 0.96 (data), 0.96 (BB)\n", - "\n", - "COSMiK extracted rules:\n", - "\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.0, 0.48], Y in [0.0, 0.48], Z0 is 1.0.\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.65, 1.0], Y in [0.0, 0.44], Z0 is -1.0.\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.94, 1.0], Y in [0.79, 1.0], Z0 is -1.0.\n", - "'Z0'(X, Y, Z0) :-\n", - " X in [0.05, 0.98], Y in [0.57, 0.99], Z0 is 0.53 - 0.82 * X - 0.28 * Y.\n" - ] - } - ], - "source": [ - "cosmik = Extractor.cosmik(predictor, max_components=4, k=150, patience=15, close_to_center=True, output=Target.REGRESSION)\n", - "theory_from_cosmik = cosmik.extract(train)\n", - "scores, completeness = get_scores(cosmik, test, predictor)\n", - "print(f'COSMiK performance ({cosmik.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCOSMiK extracted rules:\\n\\n' + pretty_theory(theory_from_cosmik))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 10, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 4.04, 4 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 2.23, 9 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 4.06, 2 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 3.79, 3 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 2.75, 5 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 2.78, 10 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 3.61, 6 rules\n", - "\n", - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 2.75, 5 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.99, 5)])\n", - "\n", - "**********************\n", - "Best Predictive loss\n", - "**********************\n", - "MAE = 2.23, 9 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (3)\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 4.06, 2 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.99, 2)])\n", - "\n", - "GridEx performance (5 rules with 100.00% coverage):\n", - "MAE = 2.79 (data), 2.79 (BB)\n", - "MSE = 14.54 (data), 14.52 (BB)\n", - "R2 = 0.42 (data), 0.42 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "'Z4'(X, Y, 3.99) :-\n", - " Y in [-0.00, 0.19].\n", - "'Z4'(X, Y, 6.53) :-\n", - " Y in [0.19, 0.4].\n", - "'Z4'(X, Y, 9.37) :-\n", - " Y in [0.4, 0.6].\n", - "'Z4'(X, Y, 11.42) :-\n", - " Y in [0.6, 0.80].\n", - "'Z4'(X, Y, 2.75) :-\n", - " Y in [0.80, 1.00].\n" - ] - } - ], - "source": [ - "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL)\n", - "pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridEx = Extractor.gridex(predictor, grid, threshold=threshold)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ORBIt performance (2 rules with 100.00% coverage):\n", - "MAE = 4.15 (data), 4.15 (BB)\n", - "MSE = 28.12 (data), 28.10 (BB)\n", - "R2 = -0.13 (data), -0.13 (BB)\n", - "\n", - "ORBIt extracted rules:\n", - "\n", - "'Z4'(X, Y, 15.42) :-\n", - " 0.02 * X - 0.03 * Y =< -0.01, -0.02 * X + 0.03 * Y =< 0.01, 1.0 * X + 0.0 * Y =< 0.46, -1.0 * X + 0.0 * Y =< -0.16.\n", - "'Z4'(X, Y, 5.053691).\n" - ] - } - ], - "source": [ - "orbit = Extractor.orbit(predictor, depth=3, error_threshold=1.0, gauss_components=2, output=Target.REGRESSION)\n", - "theory_from_orbit = orbit.extract(train)\n", - "scores, completeness = get_scores(orbit, test, predictor)\n", - "print(f'ORBIt performance ({orbit.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nORBIt extracted rules:\\n\\n' + pretty_theory(theory_from_orbit))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 14, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (3 rules with 100.00% coverage):\n", - "MAE = 0.68 (data), 0.71 (BB)\n", - "MSE = 2.05 (data), 2.06 (BB)\n", - "R2 = 0.92 (data), 0.92 (BB)\n", - "\n", - "CReEPy extracted rules (ExACT):\n", - "\n", - "'Z4'(X, Y, Z4) :-\n", - " X in [0.15, 0.84], Y in [-0.00, 0.59], Z4 is 7.49 - 7.63 * X + 12.05 * Y.\n", - "'Z4'(X, Y, Z4) :-\n", - " X in [0.15, 0.84], Y in [-0.00, 0.84], Z4 is 9.0 - 12.0 * X + 15.0 * Y.\n", - "'Z4'(X, Y, Z4) :-\n", - " Y in [-0.00, 1.00], Z4 is 2.0 + 4.0 * X - 3.0 * Y.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=3, error_threshold=0.02, output=Target.REGRESSION,\n", - " clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules (ExACT):\\n\\n' + pretty_theory(theory_from_creepy))" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CReEPy performance (4 rules with 100.00% coverage):\n", - "MAE = 3.37 (data), 3.47 (BB)\n", - "MSE = 18.62 (data), 19.19 (BB)\n", - "R2 = 0.94 (data), 0.93 (BB)\n", - "\n", - "CReEPy extracted rules (CREAM):\n", - "\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 32.45], V in [34.02, 50.16], AP in [997.90, 1026.41], RH in [35.63, 100.10], PE is 502.53 - 2.16 * AP - 0.26 * AT + 0.01 * RH - 0.11 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 35.77], V in [25.35, 81.56], AP in [997.84, 1026.45], RH in [25.55, 100.12], PE is 234.73 - 1.42 * AP - 0.29 * AT + 0.26 * RH - 0.12 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [3.30, 14.60], V in [34.68, 44.47], AP in [1011.31, 1033.25], RH in [58.98, 98.68], PE is 720.26 - 2.20 * AP - 0.47 * AT - 0.18 * RH - 0.22 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15], PE is 579.01 - 2.05 * AP - 0.60 * AT - 0.05 * RH + 0.00 * V.\n" - ] - } - ], - "source": [ - "creepy = Extractor.creepy(predictor, depth=2, error_threshold=0.02, output=Target.REGRESSION,\n", - " clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules (CREAM):\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 3.52, 2 rules\n", - "Algorithm.ExACT. Depth: 1. Threshold = 0.00. MAE = 3.54, 2 rules\n", - "\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 3.52, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 3.48, 3 rules\n", - "Algorithm.ExACT. Depth: 2. Threshold = 0.00. MAE = 3.50, 3 rules\n", - "\n", - "Algorithm.ExACT. Depth: 3. Threshold = 0.00. MAE = 3.44, 4 rules\n", - "Algorithm.ExACT. Depth: 3. Threshold = 0.00. MAE = 3.41, 4 rules\n", - "Algorithm.ExACT. Depth: 3. Threshold = 0.00. MAE = 3.52, 4 rules\n", - "\n", - "**********************\n", - "Best Algorithm.ExACT\n", - "**********************\n", - "MAE = 3.41, 4 rules\n", - "Threshold = 0.00\n", - "Depth = 3\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 3.41, 4 rules\n", - "Threshold = 0.00\n", - "Depth = 3\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 3.54, 2 rules\n", - "Threshold = 0.00\n", - "Depth = 1\n", - "\n", - "CReEPy performance (4 rules with 100.00% coverage):\n", - "MAE = 3.37 (data), 3.46 (BB)\n", - "MSE = 18.48 (data), 19.00 (BB)\n", - "R2 = 0.94 (data), 0.94 (BB)\n", - "\n", - "CReEPy extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 32.45], V in [35.39, 50.16], AP in [998.07, 1026.40], RH in [35.63, 100.10], PE is 499.89 - 2.16 * AP - 0.27 * AT + 0.01 * RH - 0.11 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 32.45], V in [34.02, 50.16], AP in [997.90, 1026.41], RH in [35.63, 100.10], PE is 697.90 - 1.74 * AP - 2.04 * AT - 0.17 * RH + 0.61 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [6.21, 35.77], V in [25.35, 81.56], AP in [997.84, 1026.45], RH in [25.55, 100.12], PE is 234.73 - 1.42 * AP - 0.29 * AT + 0.26 * RH - 0.12 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15], PE is 628.20 - 2.19 * AP - 0.50 * AT - 0.09 * RH - 0.17 * V.\n" - ] - } - ], - "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.5,\n", - " algorithm=CRASH.Algorithm.ExACT, output=Target.REGRESSION)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.exact)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 10.29, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 8.46, 2 rules\n", - "Algorithm.CREAM. Depth: 1. Threshold = 0.00. MAE = 10.29, 2 rules\n", - "\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 6.46, 4 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 6.10, 4 rules\n", - "Algorithm.CREAM. Depth: 2. Threshold = 0.00. MAE = 7.75, 4 rules\n", - "\n", - "Algorithm.CREAM. Depth: 3. Threshold = 0.00. " - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_14404/1950581394.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mcrash\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCRASH\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmax_depth\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m3\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpatience\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreadability_tradeoff\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;36m.75\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0malgorithm\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mCRASH\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mAlgorithm\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCREAM\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 2\u001B[1;33m \u001B[0mcrash\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msearch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 3\u001B[0m \u001B[1;33m(\u001B[0m\u001B[0m_\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0m_\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdepth\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthreshold\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mcrash\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mget_best\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 4\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 5\u001B[0m creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\tuning\\crash\\__init__.py\u001B[0m in \u001B[0;36msearch\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 23\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 24\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0msearch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 25\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mparams\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__search_depth\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 26\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 27\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__search_depth\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\tuning\\crash\\__init__.py\u001B[0m in \u001B[0;36m__search_depth\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdepth\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mrange\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmax_depth\u001B[0m \u001B[1;33m+\u001B[0m \u001B[1;36m1\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__search_threshold\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdepth\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[0mb\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mOptimizer\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_best\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mp\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[0mprint\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\tuning\\crash\\__init__.py\u001B[0m in \u001B[0;36m__search_threshold\u001B[1;34m(self, depth)\u001B[0m\n\u001B[0;32m 56\u001B[0m \u001B[0mclustering\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mClustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcream\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0malgorithm\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mCRASH\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mAlgorithm\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCREAM\u001B[0m \u001B[1;32melse\u001B[0m \u001B[0mClustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mexact\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 57\u001B[0m )\n\u001B[1;32m---> 58\u001B[1;33m \u001B[0m_\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mextractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 59\u001B[0m mae, n = (extractor.mae(self.dataframe, self.predictor) if self.objective == Objective.MODEL else\n\u001B[0;32m 60\u001B[0m extractor.mae(self.dataframe)), extractor.n_rules\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\creepy\\__init__.py\u001B[0m in \u001B[0;36m_extract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 32\u001B[0m \u001B[1;32mraise\u001B[0m \u001B[0mTypeError\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;34m\"clustering must be a HyperCubeClustering\"\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 33\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 34\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 35\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_hypercubes\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mclustering\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mget_hypercubes\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 36\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mcube\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_hypercubes\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\exact\\__init__.py\u001B[0m in \u001B[0;36mfit\u001B[1;34m(self, dataframe)\u001B[0m\n\u001B[0;32m 58\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_predictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 59\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_hypercubes\u001B[0m \u001B[1;33m=\u001B[0m\u001B[0;31m \u001B[0m\u001B[0;31m\\\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 60\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mNode\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 61\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget_hypercubes\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mIterable\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mHyperCube\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\cream\\__init__.py\u001B[0m in \u001B[0;36m_iterate\u001B[1;34m(self, surrounding)\u001B[0m\n\u001B[0;32m 52\u001B[0m \u001B[0mgauss_params\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mselect_gaussian_mixture\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgauss_components\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 53\u001B[0m \u001B[0mgauss_pred\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mgauss_params\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 54\u001B[1;33m \u001B[0mcubes\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__eligible_cubes\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mgauss_pred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnode\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgauss_params\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 55\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mcubes\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m<\u001B[0m \u001B[1;36m1\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 56\u001B[0m \u001B[1;32mcontinue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\cream\\__init__.py\u001B[0m in \u001B[0;36m__eligible_cubes\u001B[1;34m(self, gauss_pred, node, clusters)\u001B[0m\n\u001B[0;32m 27\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdf\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m==\u001B[0m \u001B[1;36m0\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 28\u001B[0m \u001B[1;32mcontinue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 29\u001B[1;33m \u001B[0minner_cube\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_create_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdf\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mclusters\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 30\u001B[0m \u001B[0mindices\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_indices\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0minner_cube\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnode\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mindices\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\exact\\__init__.py\u001B[0m in \u001B[0;36m_create_cube\u001B[1;34m(self, dataframe, clusters)\u001B[0m\n\u001B[0;32m 49\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_create_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mpd\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mDataFrame\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mclusters\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mint\u001B[0m\u001B[1;33m)\u001B[0m \u001B[1;33m->\u001B[0m \u001B[0mClosedCube\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 50\u001B[0m \u001B[0mdata\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mExACT\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_remove_string_label\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 51\u001B[1;33m \u001B[0mdbscan_pred\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mDBSCAN\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0meps\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mselect_dbscan_epsilon\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mclusters\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 52\u001B[0m return HyperCube.create_surrounding_cube(\n\u001B[0;32m 53\u001B[0m \u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mnp\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mwhere\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdbscan_pred\u001B[0m \u001B[1;33m==\u001B[0m \u001B[0mCounter\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdbscan_pred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmost_common\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\clustering\\utils.py\u001B[0m in \u001B[0;36mselect_dbscan_epsilon\u001B[1;34m(data, clusters)\u001B[0m\n\u001B[0;32m 30\u001B[0m \u001B[0mepsilon\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmax\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdistances\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;36m1e-3\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 31\u001B[0m \u001B[0mk\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;36m1.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 32\u001B[1;33m \u001B[0mdbscan_pred\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mDBSCAN\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0meps\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mepsilon\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mk\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdata\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 33\u001B[0m \u001B[1;31m# while Counter(dbscan_pred).most_common(1)[0][0] == -1:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mi\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mrange\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m1000\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\cluster\\_dbscan.py\u001B[0m in \u001B[0;36mfit_predict\u001B[1;34m(self, X, y, sample_weight)\u001B[0m\n\u001B[0;32m 456\u001B[0m \u001B[0mCluster\u001B[0m \u001B[0mlabels\u001B[0m\u001B[1;33m.\u001B[0m \u001B[0mNoisy\u001B[0m \u001B[0msamples\u001B[0m \u001B[0mare\u001B[0m \u001B[0mgiven\u001B[0m \u001B[0mthe\u001B[0m \u001B[0mlabel\u001B[0m \u001B[1;33m-\u001B[0m\u001B[1;36m1.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 457\u001B[0m \"\"\"\n\u001B[1;32m--> 458\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msample_weight\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0msample_weight\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 459\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mlabels_\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 460\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\cluster\\_dbscan.py\u001B[0m in \u001B[0;36mfit\u001B[1;34m(self, X, y, sample_weight)\u001B[0m\n\u001B[0;32m 404\u001B[0m \u001B[0mneighbors_model\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 405\u001B[0m \u001B[1;31m# This has worst case O(n^2) memory complexity\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 406\u001B[1;33m \u001B[0mneighborhoods\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mneighbors_model\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mradius_neighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mFalse\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 407\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 408\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0msample_weight\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mradius_neighbors\u001B[1;34m(self, X, radius, return_distance, sort_results)\u001B[0m\n\u001B[0;32m 1167\u001B[0m \u001B[0mn_jobs\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0meffective_n_jobs\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1168\u001B[0m \u001B[0mdelayed_query\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mdelayed\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0m_tree_query_radius_parallel_helper\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1169\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 1170\u001B[0m delayed_query(\n\u001B[0;32m 1171\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mradius\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msort_results\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0msort_results\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1041\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1042\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1043\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1044\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1045\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 859\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 860\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 861\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 862\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 863\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 777\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 778\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 779\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 780\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 781\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 570\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 571\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 572\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 573\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 574\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 260\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 261\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 262\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 263\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 264\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 260\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 261\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 262\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 263\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 264\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\fixes.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 115\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 116\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 117\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 118\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 119\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_radius_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 949\u001B[0m \u001B[0mcloudpickle\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 950\u001B[0m \"\"\"\n\u001B[1;32m--> 951\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery_radius\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 952\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 953\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " - ] - } - ], - "source": [ - "crash = CRASH(predictor, train, max_depth=3, patience=1, readability_tradeoff=.75, algorithm=CRASH.Algorithm.CREAM)\n", - "crash.search()\n", - "(_, _, depth, threshold) = crash.get_best()[0]\n", - "\n", - "creepy = Extractor.creepy(predictor, depth=depth, error_threshold=threshold, output=Target.REGRESSION,\n", - " clustering=Clustering.cream)\n", - "theory_from_creepy = creepy.extract(train)\n", - "scores, completeness = get_scores(creepy, test, predictor)\n", - "print(f'CReEPy performance ({creepy.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nCReEPy extracted rules:\\n\\n' + pretty_theory(theory_from_creepy))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "it = Extractor.iter(predictor, min_update=1.0 / 10, n_points=1, max_iterations=600,\n", - " min_examples=100, threshold=5)\n", - "theory_from_iter = it.extract(train)\n", - "scores, completeness = get_scores(it, test, predictor)\n", - "print(f'ITER performance ({it.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nITER extracted rules:\\n\\n' + pretty_theory(theory_from_iter))" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 6.45, 15 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (2). Threshold = 0.00. MAE = 6.45, 30 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 6.45, 86 rules\n", - "Algorithm.GRIDEX. Grid (1). Fixed (3). Threshold = 0.00. MAE = 6.45, 142 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 6.45, 144 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 2)]). Threshold = 0.00. MAE = 6.45, 146 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 2)]). Threshold = 0.00. MAE = 6.45, 150 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 2)]). Threshold = 0.00. MAE = 6.45, 154 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 6.45, 157 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 3)]). Threshold = 0.00. MAE = 6.45, 160 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 3)]). Threshold = 0.00. MAE = 6.45, 169 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 3)]). Threshold = 0.00. MAE = 6.45, 178 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 6.45, 183 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 5)]). Threshold = 0.00. MAE = 6.45, 188 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 5)]). Threshold = 0.00. MAE = 6.45, 209 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.3, 5)]). Threshold = 0.00. MAE = 6.45, 230 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 6.45, 240 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.99, 10)]). Threshold = 0.00. MAE = 6.45, 250 rules\n", - "\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 6.45, 256 rules\n", - "Algorithm.GRIDEX. Grid (1). Adaptive ([(0.33, 2), (0.67, 3)]). Threshold = 0.00. MAE = 6.45, 262 rules\n", - "\n", - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 6.45, 256 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridEx performance (277 rules with 99.90% coverage):\n", - "MAE = 6.50 (data), 6.49 (BB)\n", - "MSE = 66.41 (data), 65.91 (BB)\n", - "R2 = 0.77 (data), 0.77 (BB)\n", - "\n", - "GridEx extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n" - ] - } - ], - "source": [ - "pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - " max_depth=1, patience=1, algorithm=PEDRO.Algorithm.GRIDEX, objective=Objective.MODEL)\n", - "pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridEx = Extractor.gridex(predictor, grid, threshold=threshold)\n", - "theory_from_gridEx = gridEx.extract(train)\n", - "scores, completeness = get_scores(gridEx, test, predictor)\n", - "print(f'GridEx performance ({gridEx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('\\nGridEx extracted rules:\\n\\n' + pretty_theory(theory_from_gridEx))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "**********************\n", - "Best Algorithm.GRIDEX\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "**********************\n", - "Best MAE \n", - "**********************\n", - "MAE = 6.45, 256 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Adaptive ([(0.33, 2), (0.67, 3)])\n", - "\n", - "**********************\n", - "Best N rules\n", - "**********************\n", - "MAE = 6.45, 15 rules\n", - "Threshold = 0.00\n", - "Iterations = 1\n", - "Strategy = Fixed (2)\n", - "\n", - "GridREx performance (292 rules with 99.90% coverage):\n", - "MAE = 6.50 (data), 6.49 (BB)\n", - "MSE = 66.41 (data), 65.91 (BB)\n", - "R2 = 0.77 (data), 0.77 (BB)\n", - "GridREx extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 482.41) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 481.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.04) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 478.54) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.40) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.33) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 479.69) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.58) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 475.51) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.59) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 477.63) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.84) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.40) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.29) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 457.77) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 461.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 463.79) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 460.57) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 459.06) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.52) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.68) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.11) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 455.09) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 451.49) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 451.23) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 456.93) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 457.31) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 456.15) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.29) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.98) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 451.08) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 447.20) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.67) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.66) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 450.45) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.79) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 443.17) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 445.23) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 437.81) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 439.21) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.28) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.04) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 443.39) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.45) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 442.41) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 440.86) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.81) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 434.24) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.45) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1006.34], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.93) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [25.55, 50.42].\n", - "'PE'(AP, AT, RH, V, 436.02) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 434.65) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1006.34, 1019.79], RH in [75.28, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.39) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [1019.79, 1033.25], RH in [50.42, 75.28].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.05) :-\n", - " AT in [2.33, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.29) :-\n", - " AT in [19.05, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.13) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 461.20) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.17) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 439.66) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.90) :-\n", - " AT in [2.33, 13.48], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.67) :-\n", - " AT in [13.48, 24.62], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.76) :-\n", - " AT in [24.62, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 475.45) :-\n", - " AT in [2.33, 13.48], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.45) :-\n", - " AT in [2.33, 13.48], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 463.56) :-\n", - " AT in [13.48, 24.62], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.52) :-\n", - " AT in [13.48, 24.62], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.06) :-\n", - " AT in [13.48, 24.62], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.94) :-\n", - " AT in [24.62, 35.77], V in [25.35, 44.09], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.07) :-\n", - " AT in [24.62, 35.77], V in [44.09, 62.82], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.18) :-\n", - " AT in [24.62, 35.77], V in [62.82, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.28) :-\n", - " AT in [2.33, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.58) :-\n", - " AT in [9.02, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 453.90) :-\n", - " AT in [15.71, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.37) :-\n", - " AT in [22.39, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.51) :-\n", - " AT in [29.08, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 485.17) :-\n", - " AT in [2.33, 9.02], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 483.31) :-\n", - " AT in [2.33, 9.02], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.27) :-\n", - " AT in [2.33, 9.02], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.21) :-\n", - " AT in [2.33, 9.02], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 472.40) :-\n", - " AT in [9.02, 15.71], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.93) :-\n", - " AT in [9.02, 15.71], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.36) :-\n", - " AT in [9.02, 15.71], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 467.00) :-\n", - " AT in [9.02, 15.71], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 457.10) :-\n", - " AT in [15.71, 22.39], V in [25.35, 36.59], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.41) :-\n", - " AT in [15.71, 22.39], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 452.81) :-\n", - " AT in [15.71, 22.39], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 448.42) :-\n", - " AT in [15.71, 22.39], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.88) :-\n", - " AT in [15.71, 22.39], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.49) :-\n", - " AT in [22.39, 29.08], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 444.57) :-\n", - " AT in [22.39, 29.08], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.40) :-\n", - " AT in [22.39, 29.08], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 435.49) :-\n", - " AT in [22.39, 29.08], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 440.89) :-\n", - " AT in [29.08, 35.77], V in [36.59, 47.84], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 443.08) :-\n", - " AT in [29.08, 35.77], V in [47.84, 59.08], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.92) :-\n", - " AT in [29.08, 35.77], V in [59.08, 70.32], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 432.88) :-\n", - " AT in [29.08, 35.77], V in [70.32, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 486.49) :-\n", - " AT in [2.33, 5.68], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 482.56) :-\n", - " AT in [5.68, 9.02], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 474.79) :-\n", - " AT in [9.02, 12.36], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 466.96) :-\n", - " AT in [12.36, 15.71], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 458.45) :-\n", - " AT in [15.71, 19.05], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 450.01) :-\n", - " AT in [19.05, 22.39], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.66) :-\n", - " AT in [22.39, 25.74], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 437.65) :-\n", - " AT in [25.74, 29.08], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 433.90) :-\n", - " AT in [29.08, 32.42], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.90) :-\n", - " AT in [32.42, 35.77], V in [25.35, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 476.95) :-\n", - " AT in [2.33, 13.48], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 477.83) :-\n", - " AT in [2.33, 13.48], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 459.42) :-\n", - " AT in [13.48, 24.62], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 446.57) :-\n", - " AT in [13.48, 24.62], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 442.25) :-\n", - " AT in [24.62, 35.77], V in [25.35, 53.46], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.31) :-\n", - " AT in [24.62, 35.77], V in [53.46, 81.56], AP in [992.88, 1033.25], RH in [25.55, 100.15].\n", - "'PE'(AP, AT, RH, V, 473.96) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 468.97) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 471.39) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 472.61) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 468.07) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 470.26) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 464.10) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 445.50) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.77) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 449.32) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 447.25) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 436.54) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 438.05) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, 441.71) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85].\n", - "'PE'(AP, AT, RH, V, 446.25) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15].\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85], PE is 462.71 - 2.15 * AP - 0.22 * AT + 0.04 * RH - 0.02 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 431.79 - 2.16 * AP - 0.45 * AT + 0.08 * RH - 0.06 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 495.74 - 1.80 * AP - 0.07 * AT + 0.00 * RH - 0.01 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 703.70 - 2.37 * AP - 0.12 * AT - 0.18 * RH - 0.11 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 540.25 - 2.54 * AP - 0.04 * AT - 0.04 * RH + 0.03 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 556.66 - 2.35 * AP - 0.04 * AT - 0.05 * RH + 0.05 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [2.33, 19.05], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 453.81 - 2.40 * AP - 0.18 * AT + 0.05 * RH - 0.04 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [25.55, 62.85], PE is 495.08 - 1.30 * AP - 0.20 * AT - 0.00 * RH - 0.10 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 394.31 - 1.06 * AP - 0.32 * AT + 0.09 * RH - 0.01 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 625.50 - 1.14 * AP + 0.09 * AT - 0.13 * RH - 0.27 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [25.35, 53.46], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 699.87 - 1.10 * AP + 0.06 * AT - 0.21 * RH - 0.05 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [25.55, 62.85], PE is 106.52 - 1.12 * AP - 0.21 * AT + 0.37 * RH - 0.04 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [992.88, 1013.07], RH in [62.85, 100.15], PE is 180.91 - 1.23 * AP - 0.29 * AT + 0.31 * RH - 0.09 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [25.55, 62.85], PE is 396.75 - 1.41 * AP - 0.28 * AT + 0.10 * RH - 0.01 * V.\n", - "'PE'(AP, AT, RH, V, PE) :-\n", - " AT in [19.05, 35.77], V in [53.46, 81.56], AP in [1013.07, 1033.25], RH in [62.85, 100.15], PE is 446.80 - 1.76 * AP - 0.30 * AT + 0.07 * RH - 0.22 * V.\n" - ] - } - ], - "source": [ - "#pedro = PEDRO(predictor, train, max_mae_increase=1.2, min_rule_decrease=0.9, readability_tradeoff=0.1,\n", - "# max_depth=2, patience=1, algorithm=PEDRO.Algorithm.GRIDREX, objective=Objective.MODEL)\n", - "#pedro.search()\n", - "(_, _, threshold, grid) = pedro.get_best()[0]\n", - "\n", - "gridREx = Extractor.gridrex(predictor, grid, threshold=threshold)\n", - "theory_from_gridREx = gridREx.extract(train)\n", - "scores, completeness = get_scores(gridREx, test, predictor)\n", - "print(f'GridREx performance ({gridREx.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('GridREx extracted rules:\\n\\n' + pretty_theory(theory_from_gridREx))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CART performance (6 rules with 100.00% coverage):\n", - "MAE = 4.46 (data), 4.49 (BB)\n", - "MSE = 32.55 (data), 32.53 (BB)\n", - "R2 = 0.89 (data), 0.89 (BB)\n", - "CART extracted rules:\n", - "\n", - "'PE'(AP, AT, RH, V, 479.15) :-\n", - " AT =< 18.25, AT =< 11.90.\n", - "'PE'(AP, AT, RH, V, 435.66) :-\n", - " AT > 18.25, V > 66.20.\n", - "'PE'(AP, AT, RH, V, 451.33) :-\n", - " AT > 18.25, V =< 66.20, AT =< 22.89.\n", - "'PE'(AP, AT, RH, V, 443.00) :-\n", - " AT > 18.25, V =< 66.20, AT > 22.89.\n", - "'PE'(AP, AT, RH, V, 467.45) :-\n", - " AT > 11.90, AT =< 15.64.\n", - "'PE'(AP, AT, RH, V, 459.70) :-\n", - " AT > 15.64.\n" - ] - } - ], - "source": [ - "cart = Extractor.cart(predictor, max_depth=5, max_leaves=6, simplify=True)\n", - "theory_from_cart = cart.extract(train)\n", - "scores, completeness = get_scores(cart, test, predictor)\n", - "print(f'CART performance ({cart.n_rules} rules with {completeness * 100:.2f}% coverage):')\n", - "print_scores(scores)\n", - "print('CART extracted rules:\\n\\n' + pretty_theory(theory_from_cart))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "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.9.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index bcf9ca55..e0309801 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 12, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -17,7 +17,9 @@ "from psyke import Extractor, Clustering, Target\n", "from psyke.extraction.hypercubic.strategy import AdaptiveStrategy\n", "from psyke.extraction.hypercubic import Grid, FeatureRanker\n", - "from psyke.utils.logic import pretty_theory" + "from psyke.utils.logic import pretty_theory\n", + "\n", + "from plot import *" ] }, { @@ -95,7 +97,114 @@ "name": "stdout", "output_type": "stream", "text": [ - "2491\n" + "2491\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2492\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2493\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2494\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2495\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2496\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2497\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2498\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2499\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2500\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2501\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2502\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2503\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2504\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2505\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2506\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2507\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n", + "2508\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "CReEPy\n" ] } ], @@ -130,7 +239,7 @@ " model = KNN(200, weights='distance', p=1).fit(scaledTrain.iloc[:, :-1], scaledTrain.iloc[:, -1])\n", " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", - " break\n", + "\n", " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", @@ -138,8 +247,7 @@ " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)\n", - " break" + " missed[name].append(miss)" ], "metadata": { "collapsed": false, @@ -150,74 +258,12 @@ }, { "cell_type": "code", - "execution_count": 19, - "outputs": [ - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_7300/1366764375.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[0mranked\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mFeatureRanker\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcolumns\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfit\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtrain\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mrankings\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 2\u001B[1;33m CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n\u001B[0m\u001B[0;32m 3\u001B[0m threshold=5, min_examples=1, normalization=normalization)\n\u001B[0;32m 4\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 5\u001B[0m \u001B[0mp\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mCART\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mscaledTest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mgridex\u001B[1;34m(predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 298\u001B[0m \"\"\"\n\u001B[0;32m 299\u001B[0m \u001B[1;32mfrom\u001B[0m \u001B[0mpsyke\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextraction\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mhypercubic\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgridex\u001B[0m \u001B[1;32mimport\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 300\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mGridEx\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthreshold\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mseed\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 301\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 302\u001B[0m \u001B[1;33m@\u001B[0m\u001B[0mstaticmethod\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\gridex\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, grid, min_examples, threshold, normalization, seed)\u001B[0m\n\u001B[0;32m 18\u001B[0m def __init__(self, predictor, grid: Grid, min_examples: int, threshold: float, normalization=None,\n\u001B[0;32m 19\u001B[0m seed=get_default_random_seed()):\n\u001B[1;32m---> 20\u001B[1;33m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mTarget\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mCONSTANT\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 21\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mgrid\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mgrid\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 22\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mmin_examples\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mmin_examples\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\extraction\\hypercubic\\__init__.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, predictor, output, discretization, normalization)\u001B[0m\n\u001B[0;32m 86\u001B[0m \u001B[1;32mclass\u001B[0m \u001B[0mHyperCubeExtractor\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mABC\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 87\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 88\u001B[1;33m \u001B[0mHyperCubePredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0moutput\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 89\u001B[0m \u001B[0mPedagogicalExtractor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__init__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mpredictor\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mdiscretization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mdiscretization\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 90\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.SafeCallWrapper.__call__\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32m_pydevd_bundle\\pydevd_cython_win32_39_64.pyx\u001B[0m in \u001B[0;36m_pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend\u001B[1;34m()\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36mdo_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[0m\n\u001B[0;32m 1145\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1146\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_threads_suspended_single_notification\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mnotify_thread_suspended\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread_id\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstop_reason\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1147\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1148\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1149\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0m_do_wait_suspend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mthread\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mevent\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0marg\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msuspend_type\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfrom_this_thread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32mC:\\Program Files\\JetBrains\\PyCharm 2021.3\\plugins\\python\\helpers\\pydev\\pydevd.py\u001B[0m in \u001B[0;36m_do_wait_suspend\u001B[1;34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[0m\n\u001B[0;32m 1160\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1161\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mprocess_internal_commands\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1162\u001B[1;33m \u001B[0mtime\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0msleep\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;36m0.01\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1163\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1164\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcancel_async_evaluation\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mget_current_thread_id\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mthread\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mstr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mid\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mframe\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " - ] - } - ], - "source": [ - "ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", - "CART = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 1)])),\n", - " threshold=5, min_examples=1, normalization=normalization)\n", - "CART.extract(scaledTrain)\n", - "p = CART.predict(scaledTest)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 17, + "execution_count": 39, "outputs": [], "source": [ - "CART.normalization" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 15, - "outputs": [ - { - "data": { - "text/plain": "(648,)" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.array(p).shape" + "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", + "pd.DataFrame(rules).to_csv('results/rules1.csv')\n", + "pd.DataFrame(missed).to_csv('results/missed1.csv')" ], "metadata": { "collapsed": false, @@ -228,19 +274,20 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 38, "outputs": [ { "data": { - "text/plain": "array([ 5.94787596e-01, 6.27852580e-01, 6.53351288e-01, 6.86756656e-01,\n 6.80554845e-01, 6.56268596e-01, 6.41509728e-01, 6.23215360e-01,\n 5.06968206e-01, 4.49547691e-01, 4.50112948e-01, 3.89409986e-01,\n 4.17987119e-01, 4.47363530e-01, 4.73589506e-01, 5.27920839e-01,\n 5.65069947e-01, 6.30275078e-01, 6.50014692e-01, 6.25667786e-01,\n 5.32084121e-01, 5.11315913e-01, 4.66818875e-01, 3.79488214e-01,\n 3.49910991e-01, 2.21345403e-01, 1.93290286e-01, 1.29067767e-01,\n 1.29278790e-01, 9.70285114e-02, 1.16471507e-01, 1.20058101e-01,\n 1.10625154e-01, 9.43989669e-02, 4.62561519e-02, 1.54927422e-02,\n -3.67243793e-02, -9.73570606e-02, -8.82136957e-02, -8.59049356e-02,\n -1.88867848e-01, -1.98258070e-01, -2.14678123e-01, -2.25880942e-01,\n -2.73753227e-01, -2.86873033e-01, -3.36773813e-01, -3.41153580e-01,\n -2.72939657e-01, -2.60564106e-01, -2.22866189e-01, -3.16103250e-01,\n -4.57077669e-01, -5.69617624e-01, -4.85024701e-01, -4.38443662e-01,\n -1.81425306e-01, 3.16564715e-02, 2.01502725e-01, 3.30505305e-01,\n 4.34886887e-01, 4.88884946e-01, 4.81334210e-01, 4.72318295e-01,\n 4.49382717e-01, 3.71368779e-01, 3.10463488e-01, 2.27584909e-01,\n 1.96014710e-01, 1.21620524e-01, 8.96840584e-02, 9.66028209e-02,\n 5.56857290e-02, 9.76252433e-02, 1.16906912e-01, 9.32035580e-02,\n 1.61667957e-01, 2.43306894e-01, 4.00764438e-01, 4.08452018e-01,\n 5.04550610e-01, 6.33769305e-01, 7.70897913e-01, 8.66805211e-01,\n 9.64611914e-01, 9.24679976e-01, 9.83779612e-01, 9.57014376e-01,\n 1.18224046e+00, 1.18196180e+00, 1.25001824e+00, 1.26741209e+00,\n 1.31358148e+00, 1.30732589e+00, 1.27459292e+00, 1.28298857e+00,\n 1.30996624e+00, 1.33409461e+00, 1.34064098e+00, 1.33456069e+00,\n 1.26926162e+00, 1.23751719e+00, 1.21728041e+00, 1.18781088e+00,\n 1.07904111e+00, 9.78348868e-01, 8.94449891e-01, 8.02642930e-01,\n 7.67312190e-01, 7.79881638e-01, 7.56459980e-01, 6.39844731e-01,\n 5.59816075e-01, 5.07320578e-01, 3.96137907e-01, 3.08059300e-01,\n 3.49538875e-01, 4.37822132e-01, 5.19863709e-01, 4.83896648e-01,\n 5.49159005e-01, 6.51009546e-01, 6.66199773e-01, 6.75533692e-01,\n 7.14284944e-01, 7.49667837e-01, 7.27343955e-01, 7.50315379e-01,\n 7.35121200e-01, 7.11411018e-01, 6.50371738e-01, 6.57852210e-01,\n 6.88303646e-01, 6.88771323e-01, 6.53846882e-01, 6.39351067e-01,\n 6.42534572e-01, 6.11897413e-01, 5.48695256e-01, 5.36093577e-01,\n 4.68813273e-01, 4.81517348e-01, 4.16924579e-01, 2.27269556e-01,\n 6.95082620e-02, 4.73304329e-02, 4.31446914e-02, 1.51598952e-03,\n -1.63872715e-01, -2.66752323e-01, -3.04611850e-01, -3.09940192e-01,\n -3.80448778e-01, -5.20578055e-01, -5.58929636e-01, -5.66185437e-01,\n -5.50515452e-01, -4.97476048e-01, -4.69612472e-01, -4.37668499e-01,\n -4.08758754e-01, -4.70707072e-01, -5.20063597e-01, -5.77177092e-01,\n -6.33710168e-01, -6.80554694e-01, -7.54318656e-01, -7.98758101e-01,\n -8.44280781e-01, -8.98363509e-01, -8.95358460e-01, -8.86503609e-01,\n -8.85599683e-01, -8.82890386e-01, -8.53614367e-01, -8.31532738e-01,\n -8.33123651e-01, -6.27513729e-01, -1.37548268e-01, 1.58522033e-01,\n 3.43769358e-01, 3.53969327e-01, 3.30450997e-01, 3.16298042e-01,\n 3.07723557e-01, 2.50430376e-01, 1.79257019e-01, 1.48908911e-01,\n 1.94124475e-01, 2.96394183e-01, 3.65626353e-01, 4.03952940e-01,\n 4.89904130e-01, 5.43864115e-01, 5.70022845e-01, 5.78188313e-01,\n 6.02378943e-01, 5.53382870e-01, 5.76370663e-01, 4.53366033e-01,\n 5.46894816e-01, 6.22814657e-01, 6.98452824e-01, 8.43786807e-01,\n 9.03241794e-01, 9.80655929e-01, 1.00069167e+00, 1.03552441e+00,\n 1.05781862e+00, 1.03407151e+00, 1.01060741e+00, 9.95349018e-01,\n 9.83748339e-01, 9.77971820e-01, 9.59232606e-01, 9.75587590e-01,\n 9.86886799e-01, 9.89016229e-01, 1.01371718e+00, 1.02127174e+00,\n 1.01666168e+00, 1.06380858e+00, 1.04014026e+00, 9.68632461e-01,\n 8.50561688e-01, 7.88313619e-01, 8.60332530e-01, 8.39510615e-01,\n 8.19082407e-01, 8.41616540e-01, 8.45556255e-01, 8.78204007e-01,\n 9.01526215e-01, 8.99408318e-01, 9.75302328e-01, 1.07239590e+00,\n 1.12769248e+00, 1.10865204e+00, 1.16262617e+00, 1.19097896e+00,\n 1.18783719e+00, 1.13230601e+00, 1.14472708e+00, 1.16748934e+00,\n 1.15556360e+00, 1.15497319e+00, 1.14661413e+00, 1.12722389e+00,\n 1.11759420e+00, 1.11397318e+00, 1.12588339e+00, 1.10233146e+00,\n 1.06748597e+00, 9.94411455e-01, 9.34246345e-01, 9.26761867e-01,\n 8.79439448e-01, 8.34147761e-01, 7.88468961e-01, 2.63101924e-01,\n 2.58441893e-01, 2.75837911e-01, 3.73833340e-01, 4.06661808e-01,\n 4.50858947e-01, 4.25019531e-01, 3.93777296e-01, 4.25283768e-01,\n 4.50114669e-01, 4.32118982e-01, 4.26723712e-01, 4.25086302e-01,\n 4.12253850e-01, 4.20772144e-01, 3.73951452e-01, 3.84730496e-01,\n 4.15677813e-01, 4.70782896e-01, 5.18673694e-01, 6.28786146e-01,\n 7.11168389e-01, 8.38840673e-01, 9.24391393e-01, 1.00049266e+00,\n 1.02386999e+00, 1.07021416e+00, 1.09681443e+00, 1.14425776e+00,\n 1.35203664e+00, 1.36045510e+00, 1.41445129e+00, 1.42926777e+00,\n 1.48989705e+00, 1.48908458e+00, 1.52517735e+00, 1.55774308e+00,\n 1.53297116e+00, 1.48283249e+00, 1.45336878e+00, 1.43025635e+00,\n 1.42162708e+00, 1.39982454e+00, 1.38697364e+00, 1.40470142e+00,\n 1.41465673e+00, 1.41616546e+00, 1.39234617e+00, 1.39974420e+00,\n 1.37379750e+00, 1.31108897e+00, 1.29610318e+00, 1.26624619e+00,\n 1.21507803e+00, 1.18620871e+00, 1.16431475e+00, 1.08350728e+00,\n 1.04351910e+00, 8.14857616e-01, 9.19351580e-01, 9.92161799e-01,\n 1.05183656e+00, 1.06302057e+00, 1.15394942e+00, 1.26940951e+00,\n 1.28522890e+00, 1.44338788e+00, 1.36122698e+00, 1.22730775e+00,\n 1.26271454e+00, 1.25683022e+00, 1.26641669e+00, 1.35523754e+00,\n 1.35363264e+00, 1.19374746e+00, 8.84222340e-01, 8.45086765e-01,\n 8.26652842e-01, 7.78535822e-01, 7.91430635e-01, 7.51209328e-01,\n 7.16570137e-01, 7.12982315e-01, 6.71535968e-01, 6.59427982e-01,\n 6.61309295e-01, 6.00194599e-01, 5.46063131e-01, 5.06583233e-01,\n 4.60146040e-01, 4.13683133e-01, 3.27972661e-01, 2.84937524e-01,\n 2.45562643e-01, 2.77127619e-01, 2.53966681e-01, 2.33893354e-01,\n 2.36568447e-01, 2.28037916e-01, 2.49038569e-01, 2.64400726e-01,\n 2.94056569e-01, 2.90707304e-01, 2.83857999e-01, 3.01860592e-01,\n 3.14841571e-01, 3.39277627e-01, 3.01466072e-01, 2.93500775e-01,\n 2.87430279e-01, 2.70941481e-01, 3.06142458e-01, 3.36453506e-01,\n 3.49177875e-01, 3.60445946e-01, 3.73710668e-01, 4.04441898e-01,\n 3.94960073e-01, 3.99827257e-01, 3.77291659e-01, 3.30380384e-01,\n 3.37732723e-01, 3.47186442e-01, 3.52084474e-01, 3.77269993e-01,\n 3.77988341e-01, 3.80380920e-01, 3.69822861e-01, 3.58249184e-01,\n 3.64697520e-01, 3.33135345e-01, 3.09091741e-01, 3.80991398e-01,\n 3.61867705e-01, 3.19234148e-01, 3.21624558e-01, 2.17452675e-01,\n 1.92589914e-01, 1.79242718e-01, 1.53009476e-01, 9.77992145e-02,\n 3.23604394e-02, -2.94612149e-02, -1.14045668e-01, -1.55128381e-01,\n -1.57616220e-01, -1.71918305e-01, -1.50894217e-01, -1.67564490e-01,\n -1.88622273e-01, -2.07652581e-01, -2.58115218e-01, -2.93005352e-01,\n -3.38270047e-01, -1.49019196e-01, -3.44280222e-01, -3.28079666e-01,\n -3.26221855e-01, -2.34694433e-01, -2.19842775e-01, -2.43728538e-01,\n -2.38287675e-01, -2.07637015e-01, -2.99053400e-01, -3.51944773e-01,\n -2.98335213e-01, -2.75216185e-01, -2.52291688e-01, -5.92295676e-01,\n -6.29698629e-01, -6.37034248e-01, -6.67193507e-01, -6.61285775e-01,\n -6.59767960e-01, -6.55582862e-01, -6.60398712e-01, -6.48089994e-01,\n -6.30606033e-01, -6.13985068e-01, -5.94299126e-01, -5.51793470e-01,\n -5.21829072e-01, -5.33152444e-01, -5.58533647e-01, -5.30664797e-01,\n -4.96012827e-01, -4.81793417e-01, -4.94722300e-01, -5.05906482e-01,\n -5.84771524e-01, -6.21891793e-01, -6.57652629e-01, -6.74505274e-01,\n -6.77925168e-01, -6.63411854e-01, -6.70761225e-01, -6.71313501e-01,\n -6.78285438e-01, -6.88366243e-01, -6.75084854e-01, -6.86645949e-01,\n -6.86878877e-01, -7.03152004e-01, -7.11550530e-01, -7.06169968e-01,\n -7.06030613e-01, -7.11075855e-01, -6.80750629e-01, -6.72296413e-01,\n -6.65302849e-01, -6.74474185e-01, -6.70617066e-01, -6.70406918e-01,\n -6.31489293e-01, -5.41195959e-01, -5.06100680e-01, -4.73968482e-01,\n -4.80026300e-01, -4.66280906e-01, -4.94750733e-01, -4.66159421e-01,\n -4.53704671e-01, -4.65426691e-01, -4.67830842e-01, -4.48499002e-01,\n -4.37999267e-01, -4.06635710e-01, -4.36565611e-01, -4.87644475e-01,\n -5.10015971e-01, -5.26553795e-01, -5.24534882e-01, -5.17204913e-01,\n -5.18734726e-01, -4.88314619e-01, -4.81448198e-01, -4.49654115e-01,\n -3.59544883e-01, -2.89470852e-01, -2.52844742e-01, -1.92796426e-01,\n 3.61641165e-02, 2.67556715e-01, 2.88468372e-01, 2.69765539e-01,\n 3.03003617e-01, 1.56430282e-01, 8.94392059e-02, 8.35489348e-02,\n 9.53608325e-02, 1.06359033e-01, 4.85854783e-02, 5.95200015e-02,\n 8.92134085e-02, 1.71655377e-01, -1.62529760e-01, -9.18029145e-02,\n -7.00795871e-03, 3.55558744e-02, 7.13713389e-02, 8.51286206e-02,\n 8.85206272e-02, 9.03776601e-02, 7.80484532e-02, 7.76571930e-02,\n 7.06369278e-02, 5.61746134e-02, 7.89197268e-02, 8.51250688e-02,\n 1.11657860e-01, 8.77699810e-02, 1.40152096e-01, 1.41833281e-01,\n 1.40840041e-01, 1.85234816e-01, 1.53057074e-01, 1.61289830e-01,\n 1.60056307e-01, 1.47913238e-01, 1.59117011e-01, 1.24454678e-01,\n 8.40377871e-02, 9.25309859e-02, 8.57329210e-02, 7.57535730e-02,\n 6.16577201e-02, 1.06938161e-01, 1.02192008e-01, 1.71101839e-01,\n 2.19090649e-01, 1.94452383e-01, 1.52915395e-01, 1.42999064e-01,\n 2.34941176e-02, -1.69292889e-02, -4.84760026e-02, -8.84035972e-02,\n -1.06477491e-01, -1.46097303e-01, -1.68977796e-01, -1.69546328e-01,\n -3.06123179e-01, -2.86830814e-01, -3.27339805e-01, -3.04824309e-01,\n -3.08424522e-01, -3.13009099e-01, -3.08204615e-01, -3.30643627e-01,\n -3.47108546e-01, -3.71663036e-01, -3.69800392e-01, -3.81838621e-01,\n -4.11920074e-01, -4.26296448e-01, -4.33323503e-01, -4.32084854e-01,\n -4.26418566e-01, -3.99080526e-01, -3.43601617e-01, -3.32431572e-01,\n -3.17056043e-01, -2.77115852e-01, -2.52202840e-01, -2.14129148e-01,\n -2.00518462e-01, -1.46597547e-01, 2.36017486e-01, 2.72685842e-01,\n 3.48128210e-01, 2.84062462e-01, 2.81911796e-01, 2.71269801e-01,\n 2.17936171e-01, 1.81334194e-01, 1.55982915e-01, 1.36845118e-01,\n 8.53148103e-02, 5.47081435e-02, -1.14402861e-02, -5.94431312e-02,\n -9.09789281e-02, -1.44687840e-01, -1.67931366e-01, -1.76196230e-01,\n -1.84964598e-01, -1.80156999e-01, -1.82591858e-01, -2.04167736e-01,\n -2.17966566e-01, -1.25904259e-01, -1.53099147e-01, -1.62306106e-01,\n -2.16528907e-01, -2.69388887e-01, -3.25151001e-01, -5.16529016e-02,\n 2.34635280e-01, 2.86861522e-01, 2.49274427e-01, 1.65100897e-01,\n 1.31739752e-01, 1.38150405e-01, 1.26449068e-01, 1.62529445e-01,\n 1.71290348e-01, 1.77941660e-01, 1.68343103e-01, 1.21794429e-01,\n 7.66499527e-02, 9.79244195e-02, 1.13147159e-01, 1.19400540e-01,\n 1.31218961e-01, 1.32184095e-01, 1.21069556e-01, 1.14229094e-01,\n 1.29832401e-01, 1.24523142e-01, 1.39992356e-01, 1.42168147e-01,\n 2.48498740e-01, 2.74234518e-01, 3.09358436e-01, 3.08036727e-01,\n 3.27261828e-01, 3.36394705e-01, 3.42936055e-01, 3.48891465e-01])" + "text/plain": " BR GridEx GridREx CART COSMiK CReEPy\n0 2491 1 13 7 5 2\n1 2492 14 14 7 6 2\n2 2493 1 14 7 7 2\n3 2494 3 14 7 5 2\n4 2495 21 14 7 7 2\n5 2496 25 8 7 7 2\n6 2497 21 37 7 7 2\n7 2498 3 12 7 8 2\n8 2499 5 14 7 9 2\n9 2500 1 13 7 6 2\n10 2501 3 14 7 5 2\n11 2502 3 14 7 4 2\n12 2503 1 5 7 4 2\n13 2504 3 14 7 7 2\n14 2505 3 14 7 6 2\n15 2506 3 14 7 7 2\n16 2507 4 12 7 7 2\n17 2508 1 5 7 6 2", + "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
BRGridExGridRExCARTCOSMiKCReEPy
02491113752
124921414762
22493114772
32494314752
424952114772
52496258772
624972137772
72498312782
82499514792
92500113762
102501314752
112502314742
12250315742
132504314772
142505314762
152506314772
162507412772
17250815762
\n
" }, - "execution_count": 16, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "model.predict(scaledTest)" + "pd.DataFrame(rules)" ], "metadata": { "collapsed": false, @@ -251,20 +298,24 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "outputs": [ { - "data": { - "text/plain": " index V model GridEx GridREx \\\n0 2016-03-04 00:00:00 410.0 518.750997 0.0 -0.381030 \n1 2016-03-04 01:00:00 400.0 522.352090 0.0 -0.407892 \n2 2016-03-04 02:00:00 395.0 525.129143 0.0 -0.433331 \n3 2016-03-04 03:00:00 408.0 528.767307 0.0 -0.461158 \n4 2016-03-04 04:00:00 406.0 528.091871 0.0 -0.477063 \n.. ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 0.0 -0.294504 \n644 2016-03-30 20:00:00 501.0 489.614882 0.0 -0.278289 \n645 2016-03-30 21:00:00 518.0 490.609540 0.0 -0.267642 \n646 2016-03-30 22:00:00 510.0 491.321955 0.0 -0.251590 \n647 2016-03-30 23:00:00 511.0 491.970557 0.0 -0.229831 \n\n CART \\\n0 [441.86581920903956, 441.86581920903956, 441.8... \n1 [441.86581920903956, 441.86581920903956, 441.8... \n2 [441.86581920903956, 441.86581920903956, 441.8... \n3 [441.86581920903956, 441.86581920903956, 441.8... \n4 [441.86581920903956, 441.86581920903956, 441.8... \n.. ... \n643 [441.86581920903956, 441.86581920903956, 441.8... \n644 [441.86581920903956, 441.86581920903956, 441.8... \n645 [441.86581920903956, 441.86581920903956, 441.8... \n646 [441.86581920903956, 441.86581920903956, 441.8... \n647 [441.86581920903956, 441.86581920903956, 441.8... \n\n COSMiK \\\n0 [340.3877551020408, 340.3877551020408, 340.387... \n1 [340.3877551020408, 340.3877551020408, 340.387... \n2 [340.3877551020408, 340.3877551020408, 340.387... \n3 [340.3877551020408, 340.3877551020408, 340.387... \n4 [340.3877551020408, 340.3877551020408, 340.387... \n.. ... \n643 [340.3877551020408, 340.3877551020408, 340.387... \n644 [340.3877551020408, 340.3877551020408, 340.387... \n645 [340.3877551020408, 340.3877551020408, 340.387... \n646 [340.3877551020408, 340.3877551020408, 340.387... \n647 [340.3877551020408, 340.3877551020408, 340.387... \n\n CReEPy \n0 [464.19181456754364, 462.0928005366921, 459.64... \n1 [464.19181456754364, 462.0928005366921, 459.64... \n2 [464.19181456754364, 462.0928005366921, 459.64... \n3 [464.19181456754364, 462.0928005366921, 459.64... \n4 [464.19181456754364, 462.0928005366921, 459.64... \n.. ... \n643 [464.19181456754364, 462.0928005366921, 459.64... \n644 [464.19181456754364, 462.0928005366921, 459.64... \n645 [464.19181456754364, 462.0928005366921, 459.64... \n646 [464.19181456754364, 462.0928005366921, 459.64... \n647 [464.19181456754364, 462.0928005366921, 459.64... \n\n[648 rows x 8 columns]", - "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.7509970.0-0.381030[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
12016-03-04 01:00:00400.0522.3520900.0-0.407892[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
22016-03-04 02:00:00395.0525.1291430.0-0.433331[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
32016-03-04 03:00:00408.0528.7673070.0-0.461158[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
42016-03-04 04:00:00406.0528.0918710.0-0.477063[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
...........................
6432016-03-30 19:00:00497.0487.5210850.0-0.294504[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6442016-03-30 20:00:00501.0489.6148820.0-0.278289[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6452016-03-30 21:00:00518.0490.6095400.0-0.267642[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6462016-03-30 22:00:00510.0491.3219550.0-0.251590[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
6472016-03-30 23:00:00511.0491.9705570.0-0.229831[441.86581920903956, 441.86581920903956, 441.8...[340.3877551020408, 340.3877551020408, 340.387...[464.19181456754364, 462.0928005366921, 459.64...
\n

648 rows × 8 columns

\n
" - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "89.05338024185028 58.58037289200033\n", + "1184045968753.4443 1184045968728.9656\n", + "69.78300681133707 39.32044206472971\n", + "99.34258321344431 70.77190676931815\n", + "65.09043620900334 30.05875280438403\n" + ] } ], "source": [ - "pd.DataFrame(predicted)" + "p = pd.DataFrame(predicted)\n", + "for e in extractors:\n", + " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())" ], "metadata": { "collapsed": false, @@ -275,10 +326,30 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "outputs": [], "source": [ - "sdsfgd" + "def plot(testB, x, pred, extracted):\n", + " missing = pd.read_csv(\"data/missing.csv\", parse_dates = [0], index_col = 0)\n", + " df = pd.read_csv(\"data/averaged1.csv\", parse_dates = [0], index_col = 0)\n", + " df.LPFnorm[missing.index] = np.nan\n", + "\n", + " for i, b in bartels.iterrows():\n", + " if b.n != testB:\n", + " continue\n", + " xminmax = [b.t0, b.t1]\n", + "\n", + " f, axes = plt.subplots(3, figsize=(10, 8)) # l x h\n", + "\n", + " myplot(axes[0], [3, 1], \"LPF\", df.index, df.LPFnorm, xminmax)\n", + " myplot(axes[0], [3, 1], \"\", missing.index, missing.LPF0, xminmax, color='r', marker='*', lw=0, size=10)\n", + " myplot(axes[1], [3, 2], \"V\", df.index, df.V, xminmax)\n", + " myplot(axes[1], [3, 2], \"\", x, pred, xminmax, color='b', marker='.', lw=0)\n", + " myplot(axes[1], [3, 2], \"\", x, extracted, xminmax, color='r', marker='.', lw=0)\n", + " myplot(axes[2], [3, 3], \"B\", df.index, df.B, xminmax)\n", + " plt.subplots_adjust(hspace=0.6)\n", + " plt.savefig(f\"plot/{b.n}.jpg\", dpi=96 * 2)\n", + " plt.show()" ], "metadata": { "collapsed": false, @@ -289,39 +360,21 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "pd.DataFrame(predicted).to_csv(\"pred.csv\")" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" + "execution_count": 37, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmUAAAHoCAYAAAAISZi8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAC8/klEQVR4nOzdd3iTVfvA8e/pYG8om7JEioAI4kYRwYELnOB4X30dIODg97r1VUFF2SAbRGQoLlQEFFRky95lbyi0tEDLKB20zf37I8MkTdukTdoU7s915Wr7JDnPSZrnyf2ccR8jIiillFJKqaIVUtQVUEoppZRSGpQppZRSSgUFDcqUUkoppYKABmVKKaWUUkFAgzKllFJKqSCgQZlSSimlVBAokqDMGBNmjHnXGDOpKPavlFJKKRVsiqqlrCywwL5/Y0w3Y8yLxphBxpjSRVQnpZRSSqkiUyRBmYicAU45bXpcRMYA64EHiqJOSimllFJFKayoK2BTyvbzBHC9+53GmB5AD4CyZcteHRUVVYhVU0oppZTKnw0bNpwUkQhvHhssQVma7WcEcMT9ThGZBEwCaNu2raxfv74Qq6aUUkoplT/GmMPePraoBvoboBvQ1BjTBphpjHkRaAv8XBR1UkoppZQqSkXSUibWVdAH2W4AG4uiHkoppZRSwULzlCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDMqWUUkqpIKBBWRBauHAht956a1FXQymllFKF6KILylasWEGtWrX4448/HNv279/PTTfdxAcffABA//79GT9+POPHj+eJJ55wef61117L3Llzs5UbHx/P/fffT8+ePZk4cSJPPvkk8fHxvPnmm9x6661MnjyZyZMn89RTT7k8z37/559/zssvv8x3332X52vo1KmT4/epU6eyb98+j487dOgQTz/9tOPv//3vf3mW7cnRo0e5//77ueeeexzbRITrrruOnj17kpyc7HVZQ4YM4cyZMwAaWCqllFI+CJbksX7Trl07KlasyB133OHY1rhxY5o0aULnzp0B+Pnnn1m8eDGVK1fm5ptvdjxu8eLFPPnkkwwfPpz77rvPpdwaNWrQpk0boqKi6N69OwcOHGDNmjV07tyZM2fO8NxzzzFw4ECmTZvm8jz7/c8//zxbt27l008/pVu3bkyfPp2UlBROnTpFhw4duPHGG/nkk08QEWrXrg3AhQsXWLZsGQD169fntddeo02bNuzevZtPPvmEZcuWsXfvXqZMmcJ1113HDz/8wMcff8z27duZPn06TZo04ejRo7z11lt0796diIgIIiMj2b9/P1OnTnXUsW7durRp04Zjx44xf/58OnfuzHfffUdUVBQdOnSgXLlyjBkzBmMM8fHxdOzYkZIlS9K9e3fee+89Fi1axKOPPsrNN9/M8uXLueaaawgJCSEuLo7Jkyfz+OOPM2TIEOrWrcvu3bt56qmniImJoW/fvvTt25cpU6YwfPhwFixYQPXq1SldujTPP/+8Xz8XSimlVLC76IIyb0yYMIHXX3+dpKQkOnToQIsWLQBYsGABgwYNYt68eWzYsIHWrVvz8ssvExoaymeffQbA0qVLSU5Opnr16txzzz0sX76c6OhoRo4cyd69ez3ub/fu3YwbN45ff/2Vzz//HIAmTZqwcuVKSpYsyfTp02nZsiXz5s1j5cqVJCcnM23aNEqUKMEtt9ziKKdu3bpYLBaWLVvG8ePHueWWW1i0aBHPPPMMALVq1QLgo48+on///jRt2pRu3bpx6NAhunbtyrlz53jppZdo3769x3oOGDCALl26cMMNN3Dy5EkaNmwIwPnz5/n2229ZsWIFKSkpdOzYkVWrVtGgQQNHMDZkyBC6dOlCmzZtALjllluoVasWzz33HLt27WLXrl188MEH7Ny5k/79+/Ptt99Srlw5evbsydNPP82ff/7J/v37uf3222nZsmVB/8VKKaVUsXPRB2V79+6lQYMG2bZPnjwZEeHOO++kXbt2lCxZkpMnTzJ16lSaN2/O0KFD+eabbxgzZozL89q3b0/37t1dtrVs2ZK+ffvmWIemTZvSu3dvUlNTmTt3Lj179qRXr16sXbuWmJgYPvroozxfx7x58zh9+jSvv/46ixYtIi0tDevCCGCxWBy/21nz8+KyvXz58tm2OatevToPP/wwjz76KD/99BNDhw51lOWpPHuZp06dIiMjI1t5xhhEBIvF4vH55cqVwxhDqVKluO6667jmmmuYOnUqX3/9NZMmTcrzPVFKKaUuJhddULZixQpOnz7tCChWr17N22+/zd69e5k/fz7XX389kydPZsuWLWRmZlKvXj1q1KjBiy++yNtvv03btm3ZtWsXN998M7Nnz6Zr166AdUzZxo0biYuLo0OHDtSoUQOA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VWP58uXExsbSqFEjJkyYAMALL7xA9+7dqVevHjExMaxbt462bdtSvnx5Nm7cSOXKlXOsT3h4uMtA9uHDh3PkyBE+/vhjwNr1+ddff2GMYd++fTRu3NhRxpo1a1iyZAl79+71ODjXLj09nbZt27J582bHtvj4eE6ePElERAQXLlygQ4cOrFixgg8//JCff/6Zl156iaVLl3rzFjscOHCAwYMH06ZNG+rWrUv58uXZv38/1atXp1q1amRlZXHnnXcycuRIoqOjueqqq8jKysIYQ2pqquNE/9prr9GgQQNH92/16tWpW7cuMTEx7Nmzh6eeeoqjR4+SkpLC7NmzHf9D5/fX/tpat27NSy+9xHPPPUeDBg14+umnadWqFU899ZTj8RqYqcJ2/vx5ypYtS506dYiPj6ds2bKcP38esI7BjI+P93qc5759+1iyZAmZmZmEh4fzyiuvEBERQb169TzmJqtWrZrLONvy5ctzzTXXuGTwr1WrFnXr1nV5Xtu2bbn22msdf4uIy4XeyZMnqVatWp71HTFiBN988w0nTpzIcViFunT4kjw2S0SGicijInK3iHQRkR4iEpgRmcrvTp48SdWqVTHGsGHDBgDHiU9EWLFihSP/zr333kuPHj3IyspyBGbdu3fn/vvvp0+fPpQsWZL27duzY8cOjh07Ru3atR2Pq1SpEqGhoVSsWJGzZ886lijp1q0bGzZs4KGHHnLUyVMAULduXbZs2UJqaio1a9Zk9+7dpKWlERcXx7x589i6dSvGGNLT02ncuLHjeWfPniUzM5P69etn6wJNSUlxnDCTkpK4/fbbXVrf3n//fU6cOEHjxo05e/YsJUqU4NixY3Tt2pXx48fz0EMPsWHDBo+zTHOydu1a3njjDa6++mrAetW8bt06atSoQadOnbjnnnuIioqiXbt2zJkzh1atWjlaCa+77joee+wxAK655hrHe2tXr149jh49yv79+2nUqBFVq1bllVde4c8//6RWrVrAPwGWMYY9e/Zw+eWXO7aHhITQtWtX6tSpA8Cdd94J/DOrTKnCdOTIESIjI6lXrx41a9akWbNm7Nq1y3E8v/766yxfvjzPcpKSkrjttttISEigWbNmLvd17NiR48ePZ3uOMYbjx487WuiefPJJbrnlFp9fw80338yyZcscf69du5arr77a44Xwzp07uXDhAgsWLOC2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7sS9td2+7qKdPZs8WINvzX9mVadOnTwnaNh7Bt5//31q1KjBiRMnaNasGVdcccUlEZAB+Q7IwDrm1fn7olatWvTv358rr7yShx9+mCVLlrg8fuLEiS7dv8ePHy/UtWt//vnnoDw+pk+f7rI4fKDs378/x+Xr8puE29fuy8+Aj0TkGRH5D/BOvvaa3U/ANcaYF4Gvc3vguXPnHCdMsLYaLV26FLCeXDMyMkhJSSE6OprGjRvTrFkz4uLiWLNmDSNGjHAMVhURIiIiOHnyJG+++SZHjx51yfnl7ueff+b111/n6aefZvr06Zw8edLxhWSf/eisZMmSpKenk5qa6tXyNnYlSpTgyJEj2bo3nV122WUuubvat29/yZzwgkmJEiUuyZmt0dHRXH755SQlJVGhQgVKlixJ5cqVeeSRRwDrlbx9hvCUKVP473//63hurVq1qF69OqGhoaSkpNC/f38sFgubNm3KFizZzZ8/n99++41GjRpx8OBBbrrpJubPn++yDundd9+d7Xn2hemdvzRmzJjBpk2bXCYL2FfjsAd0VatWdYz1XLZsWbbALticP3+ed955h4ULF2b70vbGokWLHF8g9nOps2PHjpGZmUmdOnU4fPgwv/76K3FxcR7LsndfhoSEONZn7dChA3fccYfP9boUlStXzmX8WHh4uKNl0hjDtddey88//8xnn32GiFCxYkWXC5Tjx487xnYWhjlz5uT4WShKxpiA5EJ0FxsbS8OGDbNNzvv222/p379/vsr0KSgT61nrGqdNnfO11+zlpojIGyIyJrdB/mC9Snc+cRw5coRTp06xePFihgwZwssvv8zOnTtJTk7mueeeo2vXrlgsFqpXr86YMWMIDw93ZGS3n3yvvfZaXnrpJTZt2sT69euzLW1RtWpVYmJiKFOmDBUqVKB69eps3ryZFi1aULNmTb+3Uh07dizHNdrAejV1//33O/5+++23/bp/pdyFh4ezfft2PvjgA/766y86dOhAQkICkZGR1K5dm//85z+O4yAqKoro6GjHBUnZsmUd5TRp0oTWrVvz8MMP06tXL1q2bMl3333n6NJ0t2HDBi5cuMD3339PZGQkVatWpUyZMowePZodO3Y4voBeeuklwDq+zDnlyUMPPcTMmTMB6/iPunXrcvDgQaZOncqJEydc9pWYmEjVqlVdgrKTJ0+6rKIRjObM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AQ3KlFJKKaWCgAZlSimllFJBQIMypZRSSqkgEBboHRhjWgMvAauAKsAhIAKoB/QDjO3nESBeRH4IdJ2UUkoppYKNEZHA7sCYSsA44ADQATgpIl2MMY8A4Vhb69JF5AdjzGwR6eqhjB5AD4CyZcteHRUVFdA6K6WUUkr5w4YNG06KSIQ3jw14SxlwHzAX+B5YBrxq234CuB5rS9kq27bSngoQkUnAJIC2bdvK+vXrA1lfpZRSSim/MMYc9vaxhTGmrBqQJCJZwOtAmm17BNYuyxjb7wCphVAfpZRSSqmgUxgtZd8BbxljmgDRwBpjzIu4jSkzxtQAvi6E+iillFJKBZ2AB2UiEgu8nMfD3gh0PZRSSimlgpmmxFBKKaWUCgIalCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDshwkJiZy//33s2TJkoDva/LkyTz99NMB349SSimlgtdFF5S98cYbdO7cmaSkJLZu3cp1110HwObNm+nevTsnTpzgww8/zLOcKlWq0KZNm0BXF4BOnToVyn6UUkopFbwKI09ZoXr//fe56667qFy5MtOnT6dKlSrExMQQEhJCz5492b9/P3PmzOH999/nhRdeICEhgeuuu46VK1fy888/ExMTwwcffMBNN93Epk2buPXWW13K79evH3Xr1mXz5s288cYb9OvXj/T0dG6++WZWrVrF2LFjOXz4MLNmzaJBgwZs27aNQYMGMWvWLE6cOAGAMYbevXvzf//3f9SrV4+srKwieKeUUkopFUwuupaycuXKUb16dQ4cOEBmZibdu3dn1qxZrFixgltuuYXrr7+ecuXKAdC9e3caNmzIm2++SdmyZYmLi2Ps2LF0796d559/niZNmmQr/8CBA5w5c4ZevXpRq1YtbrnlFm688UZeeOEFWrduzffff89HH31EmTJlEBFSU1OJi4ujX79+lC1blrJlyxIdHc2OHTuIi4vjv//9L507dy7st0kppZRSQeaiaykDeOihh+jfvz+PP/441113HQ8++CAPPvggoaGh2R5bvnx5AEqUKEFGRkaeZQ8dOpRDhw7x2muv8c4777jcZ1/c3RhDp06daN26NU2aNKFq1aoA/Pvf/yYkJIR69eoV9CUqpZRS6iJzUQZl9913H2+88QZffPEFYWFhlC5dmiuuuAKA1atXExcXx+rVq1myZAkbN25k7969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eTInTpww+by4f/8+Q4cOZePGjfTq1euBxzdu3LjAG2128oqjoyPnz5/X3zRz8thjj2nSJeDKlSv5GuPFldTUVL0HNqeBHRgYyLFjx4x+JiMjg5kzZzJlyhQ6duyIg4MD5cqV45tvvmHEiBF06dKFgIAAGjZsyBdffMG4ceM0e0Av6CGgfv36nDlzxuIxQJdYExUVRalSpXjsscdYvXo1TZo00e9v164dW7Zseeg7bZiLMspszM2bN/ONDQkODubAAW17vm/atIlu3brRtGlT+vbty/Tp07l//z6bNm2ia9eugC7oesuWLUydOpXmzZszcuRIxo4dy7179/LUtPnxxx/1nzOGq6urPgC+dOnSDBgwgKFDh+Lh4YGbmxvvvPMOLVq0ICQkpFD/j2bNmtGpUyeOHTvGzp07jR6TlpbGihUrjLr/c9+swsPD6dq1KxcuXAAKt9ySk379+tGsWTN9PODhw4cZOXIk27Zto1+/fkVaYsASpJT6uEYpZaEuoFJKZs2axTPPPMOhQ4c4efIkf/75JwEBAfTt27fAz2bHXRmLK7p165bZRlmpUqUYOHAgJUuWNLo/KSmJ1atXm5URmk22J8/d3T3fxJBs/P39efvtt0lNTWXp0qUmyf/777955ZVXTC4b0bJlS37//fd896empupj3hwdHalWrVqeY/z8/IiMjLTYi5KcnIy7u7tFMuyNI0eO6GvC5cxqbNGiBbt37yYhISHPZ+bOncvgwYOZOHGiPnO2c+fOpKSkGJybzs7OfPzxx5QuXZru3btr4i2LiYkxGioDuvp/WpWr2LNnDy+99BIAlStX5t69e7i6uur3CyEYPnw406dPL5axg9ZGGWU25v79+/lmWtapU4dz587pvQ1acP36df2yR7ly5RgwYADz588nOjpav7wghOC9995j4MCBBksOL730EocPH9ZP3piYGIQQeHp6WqRTlSpV9B6SbO7fv2/Ue7Zu3ToaNmyof//hhx9y8eJFAH7//XcWLlzIpEmTOHfuHDNmzKBVq1Zs3bqVmTNnkl14OCYmho8//tjgInTy5EmefPJJ/c00MTHRrKBygFatWrF//35A99RYvnx5wsLCaNy4sdUKNVqDatWqcenSJbZs2cKCBQtM/tzevXtp3749vr6+vPnmmxw/fpyffvqJVq1a6YP886NChQpERERQt27dPMtmd+7c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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "pd.DataFrame(rules).to_csv('rules.csv')\n", - "pd.DataFrame(missed).to_csv('missed.csv')" ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], "source": [ - "p = pd.DataFrame(predicted)" + "plot(2492, p['index'], p.model, p.GridREx)" ], "metadata": { "collapsed": false, @@ -335,7 +388,22 @@ "execution_count": null, "outputs": [], "source": [ - "abs(p.COSMiK - p.model).mean()" + "[\n", + " ('half2', 1.0),\n", + " ('Bmin', 0.9014926374248455),\n", + " ('Bmedian', 0.5931779258652916),\n", + " ('Bmax', 0.5580664110760513),\n", + "\n", + " ('trend', 0.441328536097858),\n", + " ('max', 0.19391808653199277),\n", + " ('median', 0.16157357528452515),\n", + "\n", + " ('halfB2', 0.06672545822303622),\n", + " ('min', 0.042323799412967836),\n", + " ('B', 0.014457395044254808),\n", + " ('halfB1', 0.005159557201584083),\n", + " ('half1', 0.0023286124765232786),\n", + "]" ], "metadata": { "collapsed": false, @@ -346,10 +414,25 @@ }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 15, + "outputs": [ + { + "data": { + "text/plain": "V 454.232082\nmodel 454.416913\nCART2 55.474434\nCART5 39.939136\nCART10 37.642059\nCART25 36.371025\nCART50 39.203983\nCART75 40.454190\nCART100 43.945508\nCART150 47.059375\nCART200 49.097016\nCART250 51.038554\nCART300 51.182619\nCART400 52.651320\nCART500 52.874986\nCART600 53.924574\nName: mean, dtype: float64" + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "p.describe()" + "import pandas as pd\n", + "\n", + "p = pd.read_csv('results/cartpredpruned.csv')\n", + "p = p[p.columns[2:]]\n", + "for c in p.columns[2:]:\n", + " p[c] = abs(p[c] - p.model)\n", + "p.describe().loc['mean']" ], "metadata": { "collapsed": false, From d398d67965b61291775c42e1f772df6e47828ee1 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 01:49:39 +0200 Subject: [PATCH 60/67] wip --- demo/DemoRegressionScaled.ipynb | 331 +++++++++++++------------------- 1 file changed, 132 insertions(+), 199 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index e0309801..3c90fa85 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 12, + "execution_count": 1, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -22,6 +22,40 @@ "from plot import *" ] }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [], + "source": [ + "def plot(testB, x, pred, extracted):\n", + " missing = pd.read_csv(\"data/missing.csv\", parse_dates = [0], index_col = 0)\n", + " df = pd.read_csv(\"data/averaged1.csv\", parse_dates = [0], index_col = 0)\n", + " df.LPFnorm[missing.index] = np.nan\n", + "\n", + " for i, b in bartels.iterrows():\n", + " if b.n != testB:\n", + " continue\n", + " xminmax = [b.t0, b.t1]\n", + "\n", + " f, axes = plt.subplots(3, figsize=(10, 8)) # l x h\n", + "\n", + " myplot(axes[0], [3, 1], \"LPF\", df.index, df.LPFnorm, xminmax)\n", + " myplot(axes[0], [3, 1], \"\", missing.index, missing.LPF0, xminmax, color='r', marker='*', lw=0, size=10)\n", + " myplot(axes[1], [3, 2], \"V\", df.index, df.V, xminmax)\n", + " myplot(axes[1], [3, 2], \"\", x, pred, xminmax, color='b', marker='.', lw=0)\n", + " myplot(axes[1], [3, 2], \"\", x, extracted, xminmax, color='r', marker='.', lw=0)\n", + " myplot(axes[2], [3, 3], \"B\", df.index, df.B, xminmax)\n", + " plt.subplots_adjust(hspace=0.6)\n", + " plt.savefig(f\"plot/{b.n}.jpg\", dpi=96 * 2)\n", + " plt.show()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "code", "execution_count": 2, @@ -48,22 +82,22 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 30, "outputs": [], "source": [ "def gridex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridEx = Extractor.gridex(model, Grid(1, AdaptiveStrategy(ranked, [(0.6, 3), (0.75, 4)])),\n", - " threshold=5, min_examples=1, normalization=normalization)\n", + " threshold=.5, min_examples=1, normalization=normalization)\n", " gridEx.extract(train)\n", " return gridEx.brute_predict(test), gridEx.n_rules, sum([p is None for p in gridEx.predict(test)])\n", " \n", "def gridrex(model, train, test, normalization):\n", " ranked = FeatureRanker(train.columns).fit(model, train.iloc[:, :-1]).rankings()\n", " gridREx = Extractor.gridrex(model, Grid(1, AdaptiveStrategy(ranked, [(0.5, 3)])),\n", - " threshold=5, min_examples=1, normalization=normalization)\n", + " threshold=.5, min_examples=1, normalization=normalization)\n", " gridREx.extract(train)\n", - " return gridREx.brute_predict(test), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", + " return gridREx.brute_predict(test, 'default'), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", "def cart(model, train, test, normalization):\n", " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", @@ -71,13 +105,13 @@ " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", "def cosmik(model, train, test, normalization):\n", - " COSMiK = Extractor.cosmik(model, max_components=10, k=100, patience=10, close_to_center=True,\n", - " output=Target.CONSTANT, normalization=normalization)\n", + " COSMiK = Extractor.cosmik(model, max_components=10, k=150, patience=10, close_to_center=True,\n", + " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", - " return COSMiK.brute_predict(test), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", + " return COSMiK.brute_predict(test, 'default'), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", "def creepy(model, train, test, normalization):\n", - " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=5, gauss_components=10,\n", + " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" @@ -91,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 31, "outputs": [ { "name": "stdout", @@ -102,110 +136,33 @@ "GridREx\n", "CART\n", "COSMiK\n", - "CReEPy\n", - "2492\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2493\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2494\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2495\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2496\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2497\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2498\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2499\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2500\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2501\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2502\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2503\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2504\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2505\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2506\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2507\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", - "CReEPy\n", - "2508\n", - "GridEx\n", - "GridREx\n", - "CART\n", - "COSMiK\n", "CReEPy\n" ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/3576493649.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/2214823521.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 34\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n\u001B[0;32m 35\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 380\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 381\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 382\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 383\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", + "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", + "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + ] } ], "source": [ @@ -240,54 +197,16 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(extractors, [gridex, gridrex, cart, cosmik, creepy]):\n", + " for name, fun in zip(extractors, [gridex, cart, cart, cosmik, creepy]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 39, - "outputs": [], - "source": [ - "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", - "pd.DataFrame(rules).to_csv('results/rules1.csv')\n", - "pd.DataFrame(missed).to_csv('results/missed1.csv')" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 38, - "outputs": [ - { - "data": { - "text/plain": " BR GridEx GridREx CART COSMiK CReEPy\n0 2491 1 13 7 5 2\n1 2492 14 14 7 6 2\n2 2493 1 14 7 7 2\n3 2494 3 14 7 5 2\n4 2495 21 14 7 7 2\n5 2496 25 8 7 7 2\n6 2497 21 37 7 7 2\n7 2498 3 12 7 8 2\n8 2499 5 14 7 9 2\n9 2500 1 13 7 6 2\n10 2501 3 14 7 5 2\n11 2502 3 14 7 4 2\n12 2503 1 5 7 4 2\n13 2504 3 14 7 7 2\n14 2505 3 14 7 6 2\n15 2506 3 14 7 7 2\n16 2507 4 12 7 7 2\n17 2508 1 5 7 6 2", - "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
BRGridExGridRExCARTCOSMiKCReEPy
02491113752
124921414762
22493114772
32494314752
424952114772
52496258772
624972137772
72498312782
82499514792
92500113762
102501314752
112502314742
12250315742
132504314772
142505314762
152506314772
162507412772
17250815762
\n
" - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pd.DataFrame(rules)" + " missed[name].append(miss)\n", + "\n", + " break" ], "metadata": { "collapsed": false, @@ -298,24 +217,69 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 24, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "89.05338024185028 58.58037289200033\n", - "1184045968753.4443 1184045968728.9656\n", - "69.78300681133707 39.32044206472971\n", - "99.34258321344431 70.77190676931815\n", - "65.09043620900334 30.05875280438403\n" + "75.32443332103857 45.80166313557053\n", + "65.44680302846326 41.183118869594864\n", + "65.44680302846326 41.183118869594864\n", + "82.47813971748161 33.34135118914507\n", + "80.09439063082522 39.23197875360054\n" ] + }, + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADP6UlEQVR4nOydd3gU5dbAf29C7713aSKINAERkCsWrlf97Fx7R4OiXntHQYqooPSOoCiKBaVI7713QugtAUINCSFlz/fH7C67yW6yLQ3O73nmSaadOTsz78yZ8573HCMiKIqiKIqiKLmPsJxWQFEURVEURfGMGmqKoiiKoii5FDXUFEVRFEVRcilqqCmKoiiKouRS1FBTFEVRFEXJpaihpiiKoiiKkkvJdkPNGFPJGDPGGLPWZVkhY8wQY8z7xphxxpj62a2XoiiKoihKbiMnPGo3A9MA47LsdeCQiPQFBgJjc0AvRVEURVGUXEW2G2oiMhWIS7P4LmClff1WoKkxpkR266YoiqIoipKbyC0xahVwN97O25cpiqIoiqJcteTLaQXsnACKu8yXsC9LhzHmReBFgKJFi7Zo2LBh1munKIqiKIoSJOvXr48VkfL+7JNbDLUZQFtgqTGmCbBZRM572lBERgGjAFq2bCnr1q3LPi0VRVEURVECxBhz0N99cmLUZ0fgCaCyMeYjY0xh4FugpjHmI+BN4Lns1ktRFEVRFCW3ke0eNRFZDCz2sKp7duuiKIqiKIqSm8ktgwkURVEURVGUNKihpiiKoiiKkktRQ01RFEVRFCWXooZaLmfevHnccsstOa2GoiiKoig5wBVtqC1btozKlSszZ84c57K9e/fSrl07Pv30UwA+++wzhg8fzvDhw3nsscfc9r/xxhv5+++/08k9fvw499xzD926dWPkyJE8/vjjHD9+nHfffZdbbrmFMWPGMGbMGJ566im3/RzrR48eTY8ePZgyZUqmv6Fz587O/ydMmMCePXs8bnfgwAGefvpp5/xHH32UqWxPHDlyhHvuuYe77rrLuUxEaN26Nd26dePChQs+yxowYADnzp0DUGNTURRFUQIgt+RRyxJuvvlmSpYsye233+5cds0111CvXj26dOkCwB9//MHChQspXbo07du3d263cOFCHn/8cb755hvuvvtuN7kVK1akefPmNGzYkK5du7Jv3z5Wr15Nly5dOHfuHM8//zz9+vXj+++/d9vPsf6FF15gy5Yt9O3bl0ceeYSJEyeSkJDAqVOn6NSpEzfddBN9+vRBRKhSpQoASUlJLFmyBICaNWvy1ltv0bx5cyIjI+nTpw9LliwhKiqKcePG0bp1a3799Vd69+7N9u3bmThxIvXq1ePIkSO89957dO3alfLly1OjRg327t3LhAkTnDpWq1aN5s2bc/ToUWbNmkWXLl2YMmUKDRs2pFOnThQrVowhQ4ZgjOH48ePceuutFCxYkK5du/Lxxx+zYMECHn74Ydq3b8/SpUtp1aoVYWFhREdHM2bMGB599FEGDBhAtWrViIyM5KmnnuLw4cO8/vrrvP7664wbN45vvvmGf/75hwoVKlC4cGFeeOGFkN4XiqIoipJXuKINNV8YMWIEb7/9NmfOnKFTp040btwYgH/++Yf+/fszffp01q9fT7NmzejRowfh4eF8++23ACxevJgLFy5QoUIF7rrrLpYuXcrWrVsZNGgQUVFRHo8XGRnJsGHDmDFjBqNHjwagXr16rFixgoIFCzJx4kSaNGnC9OnTWbFiBRcuXOD777+nQIECdOjQwSmnWrVq2Gw2lixZQkxMDB06dGDBggU8++yzAFSuXBmAXr168dlnn9GgQQMeeeQRDhw4wP/93/8RFxfHq6++SseOHT3q+cUXX3DvvffStm1bYmNjqV27NgDx8fH8/PPPLFu2jISEBG699VZWrlxJrVq1nAbagAEDuPfee2nevDkAHTp0oHLlyjz//PPs2rWLXbt28emnn7Jz504+++wzfv75Z4oVK0a3bt14+umnmTt3Lnv37uW2226jSZMmwV5iRVEURcmzXFWGWlRUFLVq1Uq3fMyYMYgId9xxBzfffDMFCxYkNjaWCRMmcN111/HVV1/x008/MWTIELf9OnbsSNeuXd2WNWnShNdff92rDg0aNCAiIoKLFy/y999/061bN15++WXWrFnD4cOH6dWrV6a/Y/r06Zw9e5a3336bBQsWkJiYiDEGAJvN5vzfgYgAuC0vXrx4umWuVKhQgQcffJCHH36Y33//na+++sopy5M8h8xTp06RnJycTp4xBhHBZrN53L9YsWIYYyhUqBCtW7emVatWTJgwgR9//JFRo0Zlek4URVEU5UrkijbUli1bxtmzZ51GxqpVq3j//feJiopi1qxZtGnThjFjxrB5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As7tmzRpQt++ff0+jmss2qxZs/jf//6HMYZy5cppnFoOsG/fvgyNqcy6XGw2GzNmzACsJMoOL63DUFu9ejVr164NSLeTJ09SvXp1Lly4AMDSpUvZtGmTx21nz57N6tWrAzqOooSSY8eOeVyekJCQYajIu+++y44dOyhdujQbN250Lj906BARERF8++23rFy5MuT6ZsSqVauc///zzz/s3r07W4+vKMFyRRlqN9xwAydPniQlJYWCBQuyd+9ewsIu/8Q6derQpEkTt33KlCnjk0fN0zbr1q2jdevWzvny5ctTpkwZSpUqFXR3meI7ixYtYsOGDR6Xr1+/niFDhvDXX3+lW3/mzBmGDx/Oxo0b+eOPPwDLUKtZsybGGJKTk6lUqRIzZ85k0aJFbp4Eh+GVGbt27eK2227jyJEjgBWv5i3P3sWLF93SzBw6dMinY4SSQA1S5crio48+8vjMK1eunNf7NzY2lqioKPbt28djjz3G1q1bnesOHjxI48aNefvtt9NV+7h06VI6WaHMc/nuu+86B5qdOXOGPXv2hEy2omQHV5ShVq1aNT744AMAKleuzK5duyhRooRz/Z133smNN96Ybr98+fJl+GCYMGECPXr0QESIiYmhXLlyAKxevZp27do5t6tRowa1a9emWbNmbl+TvrBs2TIdjOAjf//9t9v8xYsX2b9/f7rzt2vXLtatW0fVqlXdKgwsXryYJUuW8MMPP9CwYUOGDBlCy5YtSU5Opnr16tSuXZtChQpx8eJFSpYsydChQ+nYsaMzkHr//v289957PukaFRVFp06dnIZaWFiYVyPeGON8oaSmpvLMM89w5swZ305KiPjzzz+z9XhK7iQ2NtZjVY7y5ctz8uRJfvvtN1JSUtzuz82bN9O+fXtOnTpFiRIluHjxIgMGDEBEOH36NKVLl6ZEiRJuXf82m41HHnkEESEhIQGbzQaQrusUrLbkL+fOnePGG290etUKFy7M8ePH/ZajKDlJUIaaMaa0MaaGfeoZIp2C0YeaNWs6/9+xYwfXXpt5tpDatWt7HJm3c+dOvvzySxo0aMA999xDVFQU8+fP59Zbb3U7poOqVavStm1b6tat6/dX2y+//OK3cXe1Mnr0aETE2T2TL18+UlNTeeONN9J9nZ88eZIyZcqQL9/lammRkZHs2LGDfPny0alTJ+666y5at25NWFgYXbt2pWbNmlSrVo0jR44476nGjRuzbds2RIRffvnFp/sKrJx7tWvXdhpq4eHhzpeRJxz306FDh+jRowdTpkxJt01iYmLAQd2ZsXfv3iyRq+QdkpKSaN68OVu3buWnn35ydlWKCOXLl+f48eMMHDiQ+fPnuw2U2bNnD/feey+7dl0e+L9gwQKnYWSMwRjj9kF16NAhbrrpJubOncu3337Ltm3bAFixYgUi4pwfP348f/75J9u3b3dLSp0ZkZGRdO3alS1btjh1cDB69Gh/T42i5AgBG2rGmLHAUmAC8D3wRIh0ChrHgyAuLo4GDRpkur2j9mdaFixYwFtvvUXbtm1p164dK1as4Ny5c5QqVYp8+fJRsmRJj/LCwsKcL+PExESfupMaNmzoFkuhWHgKPr548SKxsbG89dZbzmU2m418+fKxYMECt21TUlK48cYbKViwIImJic5tU1JSnN3iDz74IE2bNqVr167OZTVq1HDreixSpAgXL15k4sSJ3HfffYSHh3vVOW05s0KFCjmP7WDixInMnDkz3b7FihUjPj6eyMhImjZt6jE34Pbt21m8eLHX4ztYt25dptukZf/+dOkRlauMPXv20KlTJ2bNmkX58uWdYQXnzp2jVq1abNmyhaZNm7JgwQK6du3KgAEDSExMJCUlhUaNGlG5cmXAev62bNkyQ0/Ytm3bePTRR9m6dSuJiYlERUWRnJzM6dOniYmJYezYscTExJAvXz7eeustRo8ezahRozzK8pTbcvfu3TRo0MDNQHO8HyZMmKBVZJQ8QTAetZIi0lhE/iUinYDnQqVUsCQmJlKkSBGefPJJChcunOn2Du+JK47G7HhxFylShHPnztGoUSPAGojQqVMnrzId+3///fcsW7YsUx1cY+mUy3z11Vdu86dPn+aGG25g9erVnDt3zmnInDhxgkceeYR9+/YBllcgf/78vPXWW9SqVYsaNWq4deUcP37czYgPCwujTJkyzvlq1aqlG4SQmJjIxYsXqV+/PmXKlPHYhZmQkMCAAQMAiI+Pd6ZtsdlsznvC0c3jMAQPHjzoTBFTpUoVoqOjOXToEDVq1KBy5crpAru3bdtG3bp1Mz1348aN86mbZ926dc6PhOTkZO2Cv0LxVrc2bSjBjh07aNSoEWFhYdx6662EhYWRmprKqVOnKF++PJGRkTz//PMUL16cpk2b8tBDDzFz5kzCw8MpWLAgPXv2BKyPz+eee469e/d6TUZ+5MgRqlatyn/+8x/+7//+j9jYWI4dO0ajRo3YuHEjSUlJzJw5kwcffBBjDIMGDaJSpUoeZU2ePNn5/5w5czh79qwzabXjNzhISEigZcuWrF+/3p9TqOQBLl68SLdu3XJajZASjHWw3RhTzGW+dLDKhIpixYpRpUoV7r33Xp+2DwsLS/dy2rhxI82bN3db9tprr3HLLbcA0KpVK6pWrepVZtWqVTl48CA2my3THGunTp2iTJkyHvW42knrZdy5cyddunRh9uzZPPzww8yePZtSpUrx+OOP06JFC2ec17x582jfvj3FihXDGEONGjU4ePAgZ8+eBaBLly60aNHC63ELFy6c7voWLVqU++67D/DuhT127JjzGKtWrXLGRObPn5/Fixdz3XXXObd1dAPNnj3b2Z1TpUoVjh07hs1mIywsjNtuu425c+cCMGbMGI4dO0ZCQgINGjRI93GRlooVK7J8+fIMtwGr0samTZu4dOkSpUqVSuf9U64MevfunW7ZyZMnGTlypNuy48ePU758eb777juMMbRq1Yr169cTGxtLuXLlOHbsGE2aNOGjjz4CoFatWvz666/861//Aqx7HeA///kPtWrVYuvWrdSvX9/tGOvWrWPgwIEcPnwYYwwNGjSgWbNmgPXhctNNN7FkyRLq1KlDYmJiug9uEXG7Ty9duuTW5Tp37lxWrFjhNM6uvfZadu7cCUCJEiXYtGkT999/v4abXGH06tWLBQsW0KpVK48DzPIqwRhqzwAnjDH7jTH7gRyv++mgbt261KpVy699RMQtv86aNWs8Djzwlfbt29O3b19uvvnmTLeNioqiQYMGTm/KpUuXeP3111m6dGnAx8/LHDp0yO0h7Bp8vHv3bm666SZWrVrFQw89xN69e7n77rupW7cuYWFhzi7qyMhIt5dD9erVWbJkCX369KFp06bcdNNNmdbxTBuH9sILLzhTvdSrV8/jMH+HN2D48OEcO3bM6bW79957+fXXX2nfvr1z2zJlynDmzBmn9w8uG2oOg7148eJcuHCBDRs2UK1aNX7++WdiY2O5+eabnV09K1as8Kh/pUqVfM4GLyKcPHmSOnXqcOHChYC6TbOb2bNn57QK2ULarnR/mTt3LsnJyR5Tv/zzzz9u96Qnrr32WiIjIzl16hRl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pQrx8fEcOXKEm2++mb179zpTqzjSyjhGnDk8AImJic5uya1bt3L99dc747Tat2/Pb7/9xo033sjmzZt58cUXadmyZbpjlyhRwtkV6Mi/FxMTQ5UqVVi3bh0333wzBw4ccHaZO9qNa1deWFgYd911V6bpODxx00030bhxY7/3y26ee+45v7vny5UrR61atXjkkUfo3LkzgFtC6+joaCpVquQ0Ijyl9UlISHCO6q5bty533XWX1wTDine2b9/OuHHjuPfeezl+/DiLFy/mqaeeIjY2FhEhMjKSxMREdu3axf79+2nWrFm6soEOwsPDSU1NxRjjzFHqaBc2m401a9bQvn37DEdg79mzhzp16jhjTtetW5ft9ZchCI+aiFwAPnDMG2PaAMHUQDoBuLaAEvZlaY87ChgF0LJlS8koaWluIyUlhfHjx1OpUiXatWtHpUqVMMZ4Tby6fv36dOtWr15NeHi423JHfqPU1FSGDx/Oli1bvJZZSU1NZd++fRkme02LL7FhIsKlS5ecgcibN2+madOmbNu2DZvNRsmSJWnfvj0iwueff86QIUOoWLEi9957L7///judOnVyJpN18Mgjj/ilZ07QoEEDxo8fT9euXZ0v7OykWbNm3Hbbbdx3333ky5ePhg0b0rZtW6exn5YmTZrw73//O8PKBvny5aNdu3bExMRw+vRp5zXYu3cvzZo1o2TJkjn60o6Pj6dhw4a0aNGCtWvXsn79egoVKuT1Xjlx4gRhYWHpYo/8ZenSpdxzzz0sXbqUAgUK8H//939s3bqVmjVr0qJFCxYsWMA///xDt27dnCPPNm3axIIFCyhUqBClSpUiKSmJBx98kHXr1hEfH88tt9zCddddR61atZg6dapPsYlgJVMODw+nQYMGzJkzhxtvvJHffvuNl156yWkgOkrXuRp+ub095RYcsWbNmzdn7dq13HLLLURGRlK9enUqV67svPcc53Px4sWULl2amjVr8sILL7B48WIqVqzotR0qntm0aRNjxoyhYMGCHDlyhJSUFJ555hkeeOAB/v77b1atWkVUVBRPPfUU+/bt47PPPmPHjh1e7+u//vqLdu3akZCQQNmyZWnUqBE2m41Nmzbx3nvvERMTQ3h4OBUrVvSYwH7NmjU0bNiQokWL0qhRI5o0aUKBAgWcMrxlHwg1fnvUjDHd7X/HuU7AkCB1WQnU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PXva9wUFBREeHk6bNm246667rlp6sWLFimRNeN+lSxcmTZrEnXfeSZMmTbySq69pi1oh2Lx5s0ProbmifPnyOZqPHZGamsrYsWPp3r07bdu2ZdSoUcV+nBW4dlduq1atWL9+PU8//TS1a9f2ynJKV/rnP//p9deIiYmxD/T9888/mTx5Mn/88YdLEza/9NJLrFq1iunTp9O1a1eHnyciVKhQgaCgIBo3bpzvsVFRUVy4cIHjx487tEB8VFQUtWvXtrd2uGPRokX079+fGjVq8J///MfetbV06VJKlCjBc889R5cuXXjrrbdo1KgRgwcPZsaMGfbF7Hv27Mlzzz3HK6+8wvHjx3n33XeJioqiT58+DB069KrXa968+VWzxLvj3LlzV21bsWIFr776KgMGDKB3795069aNcePGcf78eVatWkXlypXtPQTh4eEebU0+ceIEvXr14sUXX6RXr16sXr06x/6ff/7ZoYXAr7R792772MbsLl68SP/+/Rk7diyHDh0iPDzcvi8qKsrloSUHDhygTp06VKpUiZ07d5KUlESZMmWoU6cOw4cPdylmYbtw4YJ9iShvMMawYsUK7r333jyPKWh97BIlStCnTx+2bNnCgQMHmDNnjqfT9Dkt1Lxs06ZN1KpVy2vxAwIC2LFjB9OmTeOLL75g/PjxTJgwIddjFy5cyMSJE5kxYwbPPPMMkZGRNG7cmBdeeKHITwrqCa5OSDx48GCqVavGXXfdlWP805YtW3Idv3Py5EmGDx/O1KlTnX6tkydPEhMT49UbSObOncu5c+f4/PPPycjIYObMmTRo0ID169dTooTzjfAiwosvvshrr73m1M83MjKSWrVqMXbsWIdeN/vAe0fcf//9/PDDD5w9e5bXX3+do0ePOpzbiRMn+PHHH/noo48oVaoUISEh3H777bRv355p06Yxfvx4goKCcm05CQwMpEuXLsycOZOHH34YsLp0AwICuPfeexkwYAAdO3YEyLX1sWbNmvY1Fd2VkZGR65x3hw4dytF6GhYWRv/+/Xn77be5ePEijz32mH1f5cqVPT5OVkQICwvLdYmy5ORkoqKiSExMZN26dbz33nv25cYuXLhg71rMPkmwMQZjDJGRkbl2NYeFhTFw4EAuXLiQ4/Pj6l3dp06d4sSJE4SEhNCxY0cOHTrEI488AkC7du2IiIgoEncvLliw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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "text/plain": "
", + "image/png": "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q65QoULO7Zs1a0bfvn39Po5rLNq0adP43//+hzGGChUqaJxaHrBr165sjamculxsNht//fUXYCVRdnhpHYba8uXLWbFiRUC6HT16lJo1a3L27FkAFi5cyNq1az1uO336dJYvXx7QcRQllBw6dMjj8uTk5GxDRd588002b95M2bJlWbNmjXP5vn37iImJYdCgQSxdujTk+mbHsmXLnP///fffbN++PVePryjBclEZai1atODo0aOkp6dTuHBhdu7cSVTUhZ9Yr149mjVr5rZPuXLlfPKoedpm5cqVtGvXzjlfsWJFypUrR5kyZYLuLlN8Z968eaxevdrj8lWrVjFkyBB+//33LOtPnjzJsGHDWLNmDb/++itgGWq1a9fGGENaWhpVqlRh6tSpzJs3z82T4DC8cmLr1q3ccMMNHDhwALDi1bzl2Tt37pxbmpl9+/b5dIxQEqhBqlxcvPvuux6feRUqVPB6/x47doy4uDh27drFgw8+yIYNG5zr9u7dS9OmTXn99dezVPs4f/58FlmhzHP55ptvOgeanTx5kh07doRMtqLkBheVoVajRg3efvttAKpWrcrWrVspVaqUc/3NN9/MVVddlWW/AgUKZPtgGD9+PD169EBESEhIoEKFCgAsX76cTp06OberVasWdevWpWXLlm5fk76waNEiHYzgI3/88Yfb/Llz59i9e3eW87d161ZWrlxJ9erV3SoMzJ8/nwULFvDNN9/QuHFjhgwZQps2bUhLS6NmzZrUrVuXIkWKcO7cOUqXLk1sbCxdunRxBlLv3r2bt956yydd4+Li6Nq1q9NQi4qK8mrEG2OcL5SMjAwef/xxTp486dtJCRG//fZbrh5PiUyOHTvmsSpHxYoVOXr0KD///DPp6elu9+e6devo3Lkzx48fp1SpUpw7d44BAwYgIpw4cYKyZctSqlQpt65/m83G/fffj4iQnJyMzWYDyNJ1ClZb8pfTp09z1VVXOb1qRYsW5fDhw37LUZS8JChDzRhT1hhTyz71CpFOwehD7dq1nf9v3ryZyy/POVtI3bp1PY7M27JlC59++imNGjXi9ttvJy4ujtmzZ3Pddde5HdNB9erV6dChA/Xr1/f7q+2HH37w27i7VBk1ahQi4uyeKVCgABkZGbzyyitZvs6PHj1KuXLlKFDgQrW0bdu2sXnzZgoUKEDXrl259dZbadeu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i3Pdr79S86+/SH/qKUY1b86hQ4d45513+P777zl9+jTly5fnoYce8uu3XIzs3LmTn3/+mVKlSpGRkcHZs2eJiYnx6dr4yvPPw9ChF+ZjYiA21lp++dDneZYRDKc7PUwszz0HV1wxlJiYmJAdX1EUJa/Yv38/EydO5IUXXqBUqVKA5bAZMWIEp0+fJioqitdff51vvvmG66+/nipVquSxxkHi+sCPjob09DxVxxizSkTa+LWTrxYdcCOWJ+0z4DBWotvHgaL+Woehmvz1qDnI7Lk6fvy4fPHFF8753377Tb766iuP8ThTpkyx4jqy8bc7A1iNEdtzz0nKU085vXAZUVEhicW6mElKSnJ2gTqKS4e6S9TX7pLoaP8LaSuKokQyycnJMmjQIFm+fLmIWBUedu3aJWfOnJGhQ4fKqVOnInOwQKDkVXZbDxCARy3KD5vuW2AZUAi4XkQ6isg4ETnnl2UYAdSqVcttvly5cjRq1Ii4uDgSEhI4d+4cL774IpUrV2bRokXs2LGDl156ibS0NPbs2UPdunUtF4yI5Y5xQbAKnxoAEcywYRQePdrpfYt69llKly6dC78y/1KsWDEKFiwIQJEiRYiJiaF///4kJiY6tzl9+jS///57wMeIjc1y6bJgDNx88x7at28f8HEURVEijaJFi9KjRw82bNjAZ599RvXq1albty4lS5bklltuYeTIkTzyyCN5rWboiI21PGmxsXmtSUD43PVpjJkAPCMiKeFVyXcC6fr0RlJSEj/++CMpKSk89thjFClSBIC///6bc+fO0aZNGz799FO6detGp06dvAvK3K/miqOPTfGbc+fOMWTIEFq2bEmXLl3o27cvHTt25MyZM9x1110By23WDDZutP43Bp57zrpEGzZsIDk5me3bt/Pwww+H6FcoiqJEFjabjagof3w2SjAE0vXpj6FWXESSAtIsTITSUAMYMGAAJUuW5Nlnn/W4fvXq1bRq1cp3ga5WgBppIWHt2rWMHz+enj17UrlyZZYtW8batWs5ffo099xzD5dddlnQx5g5cyYnT56kVKlS3HzzzSHQWlEURVHCbKhFIqE21EaNGsV9992nXZP5EBGhb9++vPXWWxw/fpyDBw/SokULv+WcOnWK7777jueeey70SiqKoiiXNGqoKZc0Bw8eZMiQIdSvX59z585RtGhRihUrRpEiRbj1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V67s9OBt5TnOrnFXnGVkZDg0niQyMpL27duTlpbGV199lWvfiRMnqFy5MlWqVHHoNY8fP86uXbs4fPgw119/PX/72994+OGHadCgASdOnKBbt27UqFHDfkdafi3XgYGBtGnThrFjx+ZbQPqjAwcOcP311wNwyy230KlTJ+bPn+/jrCw//vgjEyZMsF80rwZxcXG53puRkZEu331sjOG9997LVVjPnj27xE1S3qNHjzznD728UJs9ezaDBw9m8ODBTJw4kTlz5vDf//6XzMxMvv76a0aNGsULL7zAwIEDGT9+PIsXL2br1q1+cz54kxZqRcAYQ0ZGhlcWrr7mmms4ceKES8/dtGlTrgkJlfOy5wM6cOCAw2OziqsjR444PL1Mo0aNuOuuuwgJCWHdunX27fPnz+fBBx90+DVr167Njh077K0cYWFh9sJl0qRJlC1blgcffJCZM2eSnp6eq7C5XNu2benTpw8hISGMHz++yO7o++OPP4iPjyc1NZVJkybl6ro5evQo58+fz/e5v/zyS65WjBo1ahAXF+fNdO1yTsdwuaNHj3Ls2DEef/xxn1woPfkzOHr0qH2ur8vXrrz++utd7jI/deoUt956K1u2bAGsaT82bNjA6tWr3U+6GMnvxqCyZcvae4MWLVpE3bp1KV26NEFBQQwdOpR77rkHgH79+tG8eXOGDBlCxYoVCQsLo3fv3kRERNCtWzeioqKu+rFuRX7XZ0m0fv16brrpJq/Erly5skszuKelpfHTTz8hIjRq1MgLmZUMUVFRHDhwgDVr1pCens5bb73l65S8pqAiKD+dOnVi8uTJVK5cmaioKIKCghweqykihISE8Msvv9C3b98r9mcvxxYQEEDv3r2ZN28eoaGhtG3bNt+YpUuXpk2bNlx33XV8+OGH9OvXz6nvJy9xcXF8+umnXHvttTz22GO59i1fvpxjx44RGhrK/v376dSpE8OHD+epp56iatWqzJ07l7CwMDIyMjDG0L59exo0aMCECRPIysri2LFjV8SsVKkS586dIzIy0u3c85Oens6ECROoWrUqISEhlC5dmh49elCunDUv+eLFi3n22WcJCgoiIcGzU14uWrSIo0ePkp6eTkJCAi+88EKuuwYPHDjAhAkTGDt2rFNxV61axQ8//MDLL79sn24ie/uhQ4do2rQpf/75JzfeeKN9X5UqVVye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j7Jw4UJ69erFwoULr5h8siBr1qyhf//+ALRu3ZrSpUuzbt06Nm7cyKBBg3j66aftBWvOOZTAKnIGDBhQ4MSjzmjfvn2uxyJS4ADz7Avj5fNlAWzfvp3p06dTrlw5MjIyCpzR/69//SvffvstqampHD58mC5dutgvgD///DOxsbG0a9eOV199Nc88VcmSc94qT8p5kapevTq7du2yDynYs2cPERERnDx5Mte5XdCHyex9tWrV4osvvuCOO+5wOJcaNWqwcuVKHnnkkVzbnZlmpmfPnnzyySccOXKE9957L88uwTFjxtiHmDg6uXlkZCTnzp3DGIOI8Msvv7B161YiIyPdvlFo//79JCUlsWbNGq677jp7d3F+EhMTCQsLIyYmxiOFuj+pV6/eFd3pXbt25e233yYoKIghQ4bkuomudOnSDBo0iIkTJxIREUGHDh349ddfadiwIXFxcQwbNoxrr72WypUrs3r1atLS0ti7dy9Dhw7lzTffJCYmhoSEBP7v//6P+vXrM2HCBF588UVOnjzp8vCiYl2ogXVxuueee5g1axYLFy4kJSWFFi1akJKSwooVKyhdujTt27enYsWKeY4byG88RP/+/XPNdlyqVCm6du3KDTfcYJ8l/YEHHmDBggU8+uij7Nq1y6V10PxR5cqVOXbsGA899BCrV6+mZcuWXh27tHr1au69916+++479u3bR7ly5ejWrRtTp07Nc6LTnLZv386KFStyfSIXEZo0aWK/ODjSohgREeH2jQTe0KRJE37++WeCg4OvuNhc7tprr2Xu3LmICEOGDOGjjz5i+fLlANx4441ev/tYKbDm4Mue965KlSq5WsdXr15Nz5497eMpJ0+eXOgceaGhoYBVqG3evNmpHot7772Xjh07OvcNXCYsLIwBAwawZ88e5s2bR3JyMqmpqQQGBhITE0P16tVp0qQJd999N+PGjXP4jluwWpTGjh1Lhw4d2LlzJ3379mXmzJlXLGVVkD/++IMpU6bw7rvvAlahPG/ePAYPHkxKSgrTp08vtCjfvn17ob+Hq0n2ON86derkOdNBUFAQL730EmD9PN9//30aNmzIrFmzmDhxIlu3bqVly5b247OL7caNGxMQEEBWVhY//PADCxcupHnz5sydO5cDBw7Y38vOKvaFGlg/9EceeQRjDGlpaU6dKPkpW7bsFdMmZP8ismUPojXG8Msvv+Q5H1FxNXDgQEqVKkXDhg0ZNWqUV2fcP336NBEREbRv395eBAcHB1O+fHn27duX5zx0GRkZ9jms+vfvn+fJ5qs78jytT58+Dk+b8vTTT3PmzBlEpNAiVylPyW5FO3DgAGvXrrW3bmdPhZEtPT2datWq2ZdsAgpcSQKgfv36REVFER4eTnJystOD3D3VtVa/fn327dvHPffcQ3h4OJcuXWLXrl289dZbfPnll4DVmu/MxbhJkybUq1ePb7/9lieffBKALl262CdOfvHFF/N97oIFC8jMzGT37t1cf/31nDt3jkuXLvH555/TvXt3AgICKFOmDJmZmblupsjLnj17rqrrlyMaNmzo0HEiQsOGDZk9ezZVq1YlMDAwV5GWfQzkXi3l7rvvtne5L1u2jD59+rg8/+FVUahly15ypijdc889LFq0iKSkJHuRcTXIPqkrV65M+/btiY2NpWXLlpw/f57Q0NBcP+f09HQ2bNjAsmXL6NKli/2T2cGDB1myZAnt27fPd8zG4cOH7WNFKlWqROfOne37evbsyWeffcbatWt5/PHHCQgIYP/+/Xz//ffEx8fTt29fh2ahL+6cWSM2MjLSb8ZIqpKjefPmjBkzhjJlytCmTZs8PyRlZWXlGkuZmJjo0Lx3OafP8PUSQdlzroE1fUyrVq347LPP7B+kCis681K6dOlcf/eCgoJ47rnn2Lp1K3PnzqVr1672fRkZGcyaNYvExESio6O55ZZbaNeuHcHBwUyZMoXw8HAGDx6cqzi96667WLVqVYGz/6ekpHhlLeqrRceOHfnxxx9d+v2C1fMHuDwn3VVVqPlC/fr12bFjR64T7Wpz8803M2HCBGJjY0lNTSUlJYUmTZpw3XXXUadOHWbOnEn9+vV56623mDBhAj/99BNBQUGkpaXRv39/Jk2aRFxcHB06dMgVNz4+nhkzZvDaa6/l+boiwlNPPcWRI0eYPHkygYGBVK9enT59+pCQkKAFiVJ+onXr1pw7dy7Pbsbs1rYVK1Zw++2327fv3bs336En+XnuuefcS9QLXO3OKkyzZs3YtWtXrsnL586dy2233UZ4eDjh4eG5CuKBAwfmGadOnTqsXLnSXqidPXuW6dOnExgYSMOGDalUqZJLK7KUNL5cn1WK6q4+b2jZsqXx90kDr1b79u3j0KFD/Pbbb4SFhfHMM88UePy6des4cOAAtWrVYufOnYgIaWlp9O3bt8SthqBUSbJhwwbWrVtHSkqKfdLkjz/+mLJly9KpU6dCB7qXZKmpqYwbN4709HTCw8PtrW3O+uijj3j++efJysrinXfeYciQIRw6dIi4uDgWL17M0KFD9c7vIiIim40xLQs/MsdztFBTRWXPnj321rirZfyYUqpwqampnD9/3j5U4eOPP8YY47U7T68m33zzDXfffbdbLXcff/wx5cqV48SJEzzwwAMeWVtTucaVQk2bMlSRqV+/vq9TUEr5QEhISK7xpCLi9PJSJVX2Yu7u6Nq1KxkZGZw7d06LtGJICzWllFJF6sSJEzqPXxHKHs/rzNJgyn9ooaaUUqpIPfHEE9SoUcPXaShVLGihppRSqkg5szKAUiVdsV7rUymllFLqaqaFmlJKKaWUn/Krrk8R6QB0BuIBY4z5l49TUkoppZTyGb8p1ESkDDAZuNEYkyoiX4tIe2PMSl/nppRSSinlC/7U9dkW+MMYk2p7vBa4z4f5KKWUUkr5lN+0qAHRQGKOxwm2bbmISG+gt+1hqoj86uE8KgKn/ThecYlZHHL0RszikKM3YhaHHL0Rszjk6I2YxSFHb8QsDjl6I2ZxyNEbMb2Ro3ML3OJfhVo8UC7H4/K2bbkYY6YAUwBEJNbZpRgK4+mYxSFHb8QsDjl6I2ZxyNEbMYtDjt6IWRxy9EbM4pCjN2IWhxy9EbM45OiNmN7K0dnn+FPX5y9ATREJsT2+GVjqw3yUUkoppXzKb1rUjDGXROR5YLyInAJ26I0ESimllCrJ/KZQAzDGrABWOPGUKV5Iw9Mxi0OO3ohZHHL0RszikKM3YhaHHL0Rszjk6I2YxSFHb8QsDjl6I2ZxyNEbMf0iRzHGeCEPpZRSSinlLn8ao6aUUkoppXLQQk0ppZRSyk/51Ri1bCJyGzAMqAXUMcak5dg3CngMeNMYM9WDr7keSLE9zDTGtPdAzHpADyAZuB34pzFmoxvxYoCVwFHbpvJYN1086UbMV4EYrLli6gC9jDHJrsazxXwJqAYkASHAUONkH7uIVAbeAZoYY1rZtpUG3gOO2XIdaYzZ62o82/ZuwHCgvzFmiQdyHAxUBuKAlljv0z1uxuwGPABsA1oB040xi12Nl2NfT+ALoJwx5qKbOT4J9OF/59A0Y8wMN2MK8HfbITFAhDHmaTdjTgOuz3FYI6CFMeawi/FqYb0nNwFNgZnGmEVu5hgD/AvYBdwIvG+M2e5gvOtt8bYA1YEzxphhIlIBGAkcxDp3XjPGnHQzZgDwLPA2cKcxxqE5LQuI9wFwCbgINAEGGGNOuBmzP9bveC/WTAIjjTG/uBMzx/7XgZeMMRXdzPGfwB05Dn3XNl7bnZjBwECsn+WNtu2vuxlzKRCW49BGQDVjTEoeYRyJ1wIYAsQCNwFj3P3diEgzoD+w2/Z9/8MYc8TBmAHAYmADEIz1d+JpIBQXzp0C4qXi7HljjPHLf8A/gY1A3xzbooH/ArHeeD0PxwvEml4kwPa4ClDJzZhRQIfLfka3uBGvMnA2R47fAD3dzLEZsC3H46+Bh1yI839Ap5y/a6yTepDt60bAajfj1QLaAauAv3kox7f539jPbsBiD8R8EqiR4+e7z514tu03AO8CBijroRxj3Hjf5BXzMeDxHI8beyBmtxxflwfmuxnvI6yLtdO/mwJiLsw+Z2zv8+1OxGsFPJDj8W6gBdbyfF1t2zoBMzwQsxlWcXoYaOiBeO/k2DYYmOCBmIOAUNu2h4AV7sa0fX0H8G/gtAdy/Kcz7xkHY/4DuC3HdofPnQJi5jx3rgM+djPeshzvc4/8brA+zDYz/3uff+NEzADgjRyPvwF6unruFBDP6fPGL1vUchgGfCgi04y1tFRf4EOskxgR+QSrdaUsEGeM+beItMX647kZq3L9P6CuMeZ8Ia/VyNYaEgpsMsa4O4dbK0CAv9vWMT0DfOJOQGPMGeAHANt8cy2NMf90I+QlIA3rgnUe6+e4y50cgdr8r8UPrE8h7YEFzgQxxnwlIndctvk+4DXb/p0i0kREyhtjElyJZ4w5BBwSkbecya2QmP/I8TAA6xOtuzE/y/GwNtYfJZfj2d6Pg4DnsP083c3R5kUROQGUASYaY866GbMn8J2I9MP6UOFUC3o+P8s5OR4+DfzHzRxPApVsX1fC+rvjVo5Yn9qzWwEOAo1FpKIxptAZ0o0xmy7bFIDVsn0fVmEO1vJ8nzuRY54xja2l2Gr4dFwB8d64bJvD504BMUfn2ObsuZNnTBG5ButD2CjgCXfjgb11LhXrA/4EY8wlN2M+AhwRkeZYH/AnuJvnZefO3x2NWUCOLp87BcS8/Ny504mYWVitdIhIKayWut+xWtOcPnfyi2eM2Wrb5mhqfl+o/Yo1EW5vEZkLZAGncuxfYoz5BkBEtonIFGPMLyKyEChjjBkkIpOxnQyFGGWM2SgigcDPIpJojPnZjdxrYq1f2sMYc0FEvsAqij5zI2ZOPYDZ7gQwxiTYuj7niEgc8Cew3828NgEjbN2UqVjdf0cLforD8ltmrNBCrajZuh6eAF7wULxQrBbUO7AKGHe8CwwzxqQ5e5EtwE/AUmPMKRG5F5iHVaC7oyZQ3lhdGnWxirYbjDGZ7iZr65boCIxzM9T7wAIReR9ojdWi6q41QBusC1dr27byOLmUjYg8BHxvjNkjIjnPnQQgUkRKGWMyXI3pzPOciSciEcDdwMOeiGnrXh6K1ZLR2Z2YWF2onwCvAOGuxLo8RxGZBxw2xiSJSF+sAqiXmzFjAGOMGSsiHYC55O5edTpmjm3lgZrGwa7uAnJ8A5htO7fbAv2cjZdHzOxzZynWuRPm7PtcRDoCL2HVF7HunjuXx3P8O/uf4nAzwb+wPv2/gtWallMVERkuIkOw/pBF5dj3G4AxZocxJr2wFzG2sWO2i8BqrC4xdyQAe4wxF2yP1+DCiVKALsCcQo8qgIg0BV4F7jPWOLfTwJvuxDTWWJ/eWE3v/bGKbYfGCDjAoWXGfM1WpH0EvG6MOeCJmMaYZGPMYKwi7b8iEuRibtcCkUA323kD8LKIuLVMijHmkDEm+0PUj8Dttg897kjAGt+BscYilgeudTNmtvuxCkt35yf6DJhqjHkZq/tmjm08mDsGAlFijfWsidUa/6czAUSkHdbfsJdsm3KeO+WBcy4UaZfHdEte8UQkHJgEPO1Mi2xBMY0xJ4wx/bE+6HzrZszmQDpWa/TzQKiIDBGROq7maIzZZYzJbkz4ESdagfKLSY5zB+vac6uz52MBv2+nWqILiLcIeNUY8wrW+NZvxclPjnnEfAxoaxubCHDc2fe5MeZ7Y8w9QC1b4ezWuZNHPKf5faFmjNkN/Ayk5Wz6F5EmWOOVXjPGjAQuH3Tq8B9gEakvIjk/wdQB3L3AbsD6Y5t9ctTE+jTmNltXyS+OFKCFqAaczfGmiwNKuxkTW8zXjTFjgQjgSw/EBOtTUlsAEckeu+NXrWm2bsWPsQaAbxYRl1oFLov5So4/YH9iLRQc6kosY8xRY8yTxpiRtvMGW64ufdLLkeMIW/M+WOfPYQ+0fK3EGguT/Sk+kCvPc1c9gWdat6/FOm8AzmG1+rv7d7Uq8J4x5gOsHoXlJscNVYURkfuwWgv7A5Vtw0Hs5w4uLM+XT0yX5RVPRCpiFWmDjDGHnD138on5ao5DDmF7P7kaEwgyxvSxnTsfAcm2c2mfGzmOyXGI09eefH439nMH69pzwJnzMb/fd46WaE+8f3KeO3FYN565G7OKMeYNY8w4rGFRztzQ1MAWM1v2+8Wlc6eAeE7zy65P26f724CyIjLUGNPTtr0SVsVcBWgI/CYiU4E9WEXH07Yuxtuwxpz96uAFKAG4T0SqYlXMR4GZ7nwPxpizYo15GyvWkliVsMbcecJz/O9uOHd8B9wrIv/GGqPWEBjggbjjRWQ1VtfnImPMb84GEJHbsf2ubU3k/8bqpnrP9rg2TnQP5BMvBXgd6w9ZNxFJN8Z872bML7B+jrVstVUY1g0V7sQMASaJyBGsmwD6O1qg5hXPGJNsO5eesx02SEQ+NsYccyPHE8BHInIIawD8ow5+ywXFHAWMFpHXsO6YesIUcodZYTFt33tTYL9x4k7XAnJ8CRggIn/BujnlNUfGkhUS8y9Y52UsUAF40Yl4LbBa2mOxbrwKwyp+XgNG2bqZrsfqoXArpojsweraD8canjLTGLPejRwnYV2TvrSdO4k4eO4UELOG7e/baaw7SZ9x7LsuMOYvIlIbqxUo1PZ7+yBHq5iz8TJEZBxWy00jrLHY7ub4KvAv23v9Bpw4Hwv6vnGhJbqAeL2xhsnsABoATzkat4CY1W2tabux3pfOXHNTgV5i3TkahPVz64c1ZMmVcyfPeCISiZPnja5MoJRSSinlp/y+61MppZRSqqTSQk0ppZRSyk8V20JNREJFZIeIvOfrXJRSSimlvKHYFmpYE8lt9XUSSimllFLeUiwLNRF5DGuG4EO+zkUppZRSyluKXaEmIg2AG4wx832di1JKKaWUNxW76TnEWhMtEGtukw5Yq9LPt02uqpRSSil11fDLCW8LYozJXhwVsdaTLKtFmlJKKaWuRsWu6zObbXmR24A2ItLD1/kopZRSSnlasev6VEoppZQqKYpti5pSSiml1NVOCzWllFJKKT+lhZpSSimllJ/SQk0ppZRSyk9poaaUUkop5aeK3TxqSimllFKeJiKrgQ1AFNAZ+MS2qxrWLBndfZKXTs+hlFJKqZJORJ4yxnwqIg2BJcaYmOztwGfGRwWTdn0qpZRSqsQzxnyaz65ywCGwijYROSEir4rIDBFZJiJdRWSaiPwsIuVtx90oItNtx00TketczUsLNaWUUkqpfBhjxuf4+lNgD7DFGPMYkAqUM8b0ArYCd9kOnQpMNsaMAWYA/3b19XWMmlJKKaWUcw7Y/j+f4+tzWK1vAI2Bu0XkNiAUuOjqC2mhppRSSinlWduB+caYHSISAjzkaiAt1JRSSimlABEJBXoD4SLytDHmPyLS1/a4B3AaqAk8KSKLsFrOHhOR48BtQCMRWQb0AgaKyH6gCjDP5Zz0rk+llFJKKf+kNxMopZRSSvkpLdSUUkoppfyUFmpKKaWUUn5KCzWllFJKKT+lhZpSSimllJ/SQk0ppZRSyk9poaaUUkop5ae0UFNKKaWU8lP/D97fQYtgwEFmAAAAAElFTkSuQmCC\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADNg0lEQVR4nOzdd3hUVfrA8e9JQu+9V2kiSJcmImtlXfVnWWUta0UUFXWt7FpQUUQsKL0j2FAsqIj03nsnhN4SIARCIKTO+/vjzgwzySSZmkzg/TzPPMncuffcMzP33nnvqUZEUEoppZRS4SeioDOglFJKKaU800BNKaWUUipMaaCmlFJKKRWmNFBTSimllApTGqgppZRSSoUpDdSUUkoppcJUvgdqxpjqxpjxxpi1LsuKG2OGG2P6G2MmGmOa5He+lFJKKaXCTUGUqF0LzACMy7IXgUMiMgj4HJhQAPlSSimllAor+R6oich0ICnL4tuAlfbXtwKtjDFl8ztvSimllFLhJFzaqFXFPXg7a1+mlFJKKXXZiiroDNidAMq4PC9rX5aNMeYp4CmAUqVKtWvWrFnoc6eUUkopFaD169fHi0gVX7YJl0BtJtAZWGqMaQlsFpGznlYUkbHAWID27dvLunXr8i+XSimllFJ+MsYc9HWbguj12R14GKhhjHnTGFMC+AKoZ4x5E3gZeCK/86WUUkopFW7yvURNRBYDiz289Gx+50UppZRSKpyFS2cCpZRSSimVhQZqSimllFJhSgM1pZRSSqkwpYFamJs3bx7XX399QWdDKaWUUgXgkg7Uli1bRo0aNZgzZ45z2d69e+natSvvvPMOAO+++y6jRo1i1KhRPPjgg27bX3PNNfz+++/Z0j1+/Dh33HEHffr0YcyYMTz00EMcP36c119/neuvv57x48czfvx4HnnkEbftHK+PGzeOfv36MW3atDzfw4033uj8f/LkyezZs8fjegcOHODRRx91Pn/zzTfzTNuTI0eOcMcdd3Dbbbc5l4kIHTt2pE+fPpw7d87rtIYMGUJiYiKABptKKaWUH8JlHLWQuPbaaylXrhw333yzc9kVV1xB48aN6dmzJwC//PILCxcupEKFCnTr1s253sKFC3nooYf47LPPuP32293SrVatGm3btqVZs2b06tWLffv2sXr1anr27EliYiJPPvkkH330EV999ZXbdo7X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12AqryoXm83GzJkzAWsQZUcprSNQW716NWvXrvUrbydPnqROnTqcO3cOgKVLl7Jp0yaP686ePZvVq1f7tR+lgunYsWMelycnJ+faVOT1119nx44dVKhQgY0bNzqXHzp0iL59+/LFF1+wcuXKoOc3N6tWrXL+/9dff7F79+583b9SgbqkArXWrVtz8uRJMjIyKFasGHv37iUi4uJbbNiwIS1btnTbpmLFil6VqHlaZ926dXTs2NH5vEqVKlSsWJHy5csHXF2mvLdo0SI2bNjgcfn69esZPnw4v/32W7bXT58+zahRo9i4cSO//PILYAVq9erVwxhDeno61atX588//2TRokVuJQmOwCsvu3bt4qabbuLIkSOA1V4tp3H2Lly44DbMzKFDh7zaRzD5G5CqS8ubb77p8ZpXuXLlHI/f+Ph4YmJi2LdvHw8++CBbt251vnbw4EFatGjBq6++mm22j9TU1GxpBXOcy9dff93Z0ez06dPs2bMnaGkrlR8uqUCtdu3a/Pe//wWgRo0a7Nq1i7Jlyzpfv/XWW7nmmmuybRcVFZXrhWHy5Mn069cPESEuLo7KlSsDsHr1arp27epcr27dujRo0IA2bdq43U16Y9myZdoZwUu///672/MLFy6wf//+bJ/frl27WLduHbVq1XKbYWDx4sUsWbKEr7/+mmbNmjF8+HDat29Peno6derUoUGDBhQvXpwLFy5Qrlw5RowYQffu3Z0Nqffv388bb7zhVV5jYmLo0aOHM1CLiIjIMYg3xjh/UDIzM3nsscc4ffq0dx9KkPz666/5uj8VnuLj4z3OylGlShVOnjzJTz/9REZGhtvxuXnzZrp168apU6coW7YsFy5cYMiQIYgICQkJVKhQgbJly7pV/dtsNu6//35EhOTkZGw2G0C2qlOwziVfJSYmcs011zhL1UqUKMHx48d9TkepghRQoGaMqWCMqWt/DAhSngLJD/Xq1XP+v2PHDq68Mu/RQho0aOCxZ97OnTv5+OOPadq0KXfccQcxMTHMnz+fG264wW2fDrVq1aJz5840atTI57u2H374wefg7nI1btw4RMRZPRMVFUVmZiYvvfRStrvzkydPUrFiRaKiLs6WFh0dzY4dO4iKiqJHjx7cdtttdOzYkYiICHr16kW9evWoXbs2R44ccR5TLVq0YNu2bYgIP/zwg1fHFVhj7jVo0MAZqEVGRjp/jDxxHE+HDh2iX79+TJs2Lds6KSkpfjfqzsvevXtDkq4qPNLS0mjbti1bt27lu+++c1ZVighVqlTh+PHjfP7558yfP9+to8yePXu488472bXrYsf/BQsWOAMjYwzGGLcbqkOHDtGlSxfmzp3LF198wbZt2wBYsWIFIuJ8PmnSJH799Ve2b9/uNih1XqKjo+nVqxdbtmxx5sFh3Lhxvn40ShUIvwM1Y8wEYCkwGfgKeDhIeQqY40KQlJRE06ZN81zfMfdnVgsWLOCVV16hc+fOdO3alRUrVpCYmEj58uWJioqiXLlyHtOLiIhw/hinpKR4VZ3UrFkzt7YUyuKp8fGFCxeIj4/nlVdecS6z2WxERUWxYMECt3UzMjK45pprKFasGCkpKc51MzIynNXi9957L61ataJXr17OZXXr1nWreixZsiQXLlxgypQp3HXXXURGRuaY56zTmRUvXty5b4cpU6bw559/Ztu2dOnSnD9/nujoaFq1auVxbMDt27ezePHiHPfvsG7dujzXyWr//mzDI6rLzJ49e+jRowezZs2iSpUqzmYFiYmJ1K9fny1bttCqVSsWLFhAr169GDJkCCkpKWRkZNC8eXNq1KgBWNff9u3b51oStm3bNh544AG2bt1KSkoKMTExpKenk5CQQFxcHBMmTCAuLo6oqCheeeUVxo0bx9ixYz2m5Wlsy927d9O0aVO3AM3x+zB58mSdRUYVCoGUqJUTkRYi8jcR6QE8EaxMBSolJYWSJUvy73//mxIlSuS5vqP0xJXjZHb8cJcsWZLExESaN28OWB0RevTokWOaju2/+uorli1blmceXNvSqYs++eQTt+cJCQm0bt2a1atXk5iY6AxkTpw4wf3338++ffsAq1SgSJEivPLKK9SvX5+6deu6VeUcP37cLYiPiIigYsWKzue1a9fO1gkhJSWFCxcu0KRJEypWrOixCjM5OZkhQ4YAcP78eeewLTabzXlMOKp5HIHgwYMHnUPE1KxZk9jYWA4dOkTdunWpUaNGtobd27Zto1GjRnl+dhMnTvSqmmfdunXOm4T09HStgr9E5TRvbdamBDt27KB58+ZERERwww03EBERQWZmJqdOnaJKlSpER0fz5JNPUqZMGVq1asU///lP/vzzTyIjIylWrBgDBgwArJvPJ554gr179+Y4GPmRI0eoVasW//jHP/i///s/4uPjOXbsGM2bN2fjxo2kpaXx559/cu+992KMYejQoVSvXt1jWt9++63z/zlz5nDmzBnnoNWO9+CQnJxM+/btWb9+vS8foSoELly4QJ8+fQo6G0EVSHSw3RhT2uV5hUAzEyylS5emZs2a3HnnnV6tHxERke3HaePGjbRt29Zt2QsvvMD1118PQIcOHahVq1aOadaqVYuDBw9is9nyHGPt1KlTVKxY0WM+LndZSxl37txJz549mT17Nvfddx+zZ8+mfPnyPPTQQ7Rr187ZzmvevHl069aN0qVLY4yhbt26HDx4kDNnzgDQs2dP2rVrl+N+S5Qoke37LVWqFHfddReQcynssWPHnPtYtWqVs01kkSJFWLx4MVdddZVzXUc10OzZs53VOTVr1uTYsWPYbDYiIiK46aabmDt3LgDjx4/n2LFjJCcn07Rp02w3F1lVq1aN5cuX57oOWDNtbNq0idTUVMqXL5+t9E9dGgYOHJht2cmTJxkzZozbsuPHj1OlShW+/PJLjDF06NCB9evXEx8fT+XKlTl27BgtW7bkzTffBKB+/fr8+OOP/O1vfwOsYx3gH//4B/Xr12fr1q00adLEbR/r1q3j888/5/DhwxhjaNq0KW3atAGsG5cuXbqwZMkSGjZsSEpKSrYbbhFxO05TU1Pdqlznzp3LihUrnMHZlVdeyc6dOwEoW7YsmzZt4u6779bmJpeY999/nwULFtChQwePHcwKq0ACtceAE8aY/caY/UCBz/vp0KhRI+rXr+/TNiLiNr7OmjVrPHY88Fa3bt0YNGgQ1157bZ7rxsTE0LRpU2dpSmpqKi+++CJLly71e/+F2aFDh9wuwq6Nj3fv3k2XLl1YtWoV//znP9m7dy+33347jRo1IiIiwllFHR0d7fbjUKdOHZYsWcKHH35Iq1at6NKlS57zeGZth9a7d2/nUC+NGzf22M3fURowatQojh075iy1u/POO/nxxx/p1q2bc92KFSty+vRpZ+kfXAzUHAF7mTJlOHfuHBs2bKB27dp8//33xMfHc+211zqrelasWOEx/9WrV/d6NHgR4eTJkzRs2JBz5875VW2a32bPnl3QWcgXWavSfTV37lzS09M9Dv3y119/uR2Tnlx55ZVER0dz6tQpKlWqxNVXX+3W5hOgf//+2YIxh3379rntwxjDihUreOmll3jmmWeyre9otxYTE0Pjxo2z9bA2xrBx40YmTJgAWCUoR48epWrVqoBVStelSxd27tzpvElu27at84e7S5cuTJ06lcaNG+dY0pf1hllnVSgczp07x/jx43niiSf8niIyHAUSqH0rIiVFpIGINMCanSAsXH/99VxxxRU+b9e/f39n6YVjBgN/VaxYkVOnTtGiRQuPY6udPn3aeTHYtm0bjRs3plmzZuzatYuYmBiuu+66Ah8csqDMmDGDzZs3k5qaSosWLdxKrtLS0ihevDi1atWiZMmSPP/8824llldffTUzZ87kjjvucEuzWLFiLFq0iIEDB9K5c2ev8vHCCy/k+Frp0qU9DtFx7Ngx7r//fsqUKcPDDz/srNKuWrUqI0aMAKwflhIlSlC3bl127Njh1taxbNmynDlzxu2YKV26NMuXL+fWW2/lpZde4oknnqBChQqcOnUKEWHKlCk5lsTmVUK7evVqmjVrhjGGuLg4GjZsSFJSEl9++aXH9UeOHJlrevlp6tSpBZ0Fv9hsNq968+7evZtz5845S64cfC11nzZtGkuWLHHeZLhKSkqiWrVqpKSksGbNGt577z3q1q3rtk6pUqVITk4mPj6eSpUq8dRTT2VL5+qrr85x/wMHDnSOTQjWueiouq9du7bbujabjYSEBBo3bszp06fp2LEjN910k9s6VatWZf78+Vy4cIHFixfz1FNPsW/fPurVq0dmZqazN/6OHTuczVNKlSrF+fPnAahXrx4bN26kRo0a2XqEg1Ut+vbbb7st83Z2kQ0bNlxSJTmFQVxcHAcPHkREaNCgAW+99Zaz48qlwu9ATUTeMMaUs8/3WdY+tVShVbp0ae6991527txJWlqaV23b8jJp0iSMMR6H//joo48YNWoUmZmZzrZMDRo0YO/evURHR9O5c+fLdiw2m83Gzp07OXXqFF26dCE6Oprp06ezaNEi5zq9e/f2uK0xho8++shjoP7ee++5DXicF9ehXTzx9IPpaMf40EMP5bhdamoqDRo0oG7duvzxxx9cffXVpKenU6RIEYwxHDx40Nl7GeDhhx/mueeeA6z3V7NmTQCuuuoqYmJiOHLkCPHx8aSlpTFv3jzndo4qX0eP5lOnTjnnMwWrNGXbtm387W9/o23btvz+++9cccUVnD592llN5CojI8OrqtT8cujQIa/Hswsn0dHRfP3118yePTvHgWVtNhsvvPACv//+O7Vr13Y71l5++WWf9le3bl2+/vprrrzyymyN540x1KlTh0OHDrF8+XIee+yxbIGRg+M6lbVJSF5cq/sB7rnnHm655RaP695zzz1Ur16d4sWL06ZNG6pVq+asEnWoXbs20dHRlCxZkg0bNvDss8/y559/0qZNG06dOsXJkyepUqUKr7/+ulvNSnJyMiVLlsQYw7///W+MMdx8883Om3OHmTNnZrtJ37lzp7NJA1jtVT2d/wcPHnQbC1GF3urVqxk+fDiHDh2iVq1aPh+fhUEgvT7vALYBk4Btxph/BC1XBeChhx6ic+fOlCpVylnCFajSpa0mfI7qLFcNGzYkIiKCVatW0b17d+Di1EUnT56kevXqORbLF1Y///yzV+s5emjGx8fTsGFDtmzZQqVKldi5c6fz4njbbbf5vH9H+8JgqVChgseSkbzu5CpXrkyDBg2oVq0aCxcupHnz5lSoUMHZmWHbtm20bt3auX5ERITHNJs0acLevXupUaMGhw4dYuHChSxZsgSwOgVERUVx4403OoO3IUOGsGLFCgYMGEBCQgIHDhzgiSesPkAdOnRgwYIFNGzYkCNHjnDllVdmq+6JjY11lkoUNJvN5lPVbig4PldPA8DmNgTL+vXrSU1NZdu2bc4q5qzzDf/yyy+88847jBkzhr/97W/O9oinT59m27ZtJCcnA94NDFutWjV27txJ27ZtOX78OMnJyQwbNowNGzZQvXp16tWrx+LFi7niiiuoU6eOW+lXKJQuXTrH2oqqVaty3333AfDaa54raerUqUNycjJt27bl2muvpXXr1qxYsYJGjRo523o65ux1Vb58eSpVqgTgnHe0ZMmSpKamEh0d7QzEYmNjnT1XHapXr+4cqHflypW8+eabHqvW4uPjc5wiTvkvJiYmx9+PuLg4+vTpw7Bhw9y+cxG5ZNp8B1L1eTNwhYhcDTQBAg7UjDE3GmNGGmMGGGPeCTQ9fzRr1oyff/45211gILIO9XD27FnKlCmDMYbo6Gi3tlCOO95LqdjWYerUqdlOnNx+aOLj46lSpQofffQRPXr0cPbgChddu3Z1ljDNnz/f6+0eeughateuTUREBPXq1aNIkSI0adLE2camUaNG2X4oPKlVqxZHjhxxHl8xMTHODhCOz65EiRKkpaUxcuRI7r//fg4dOkRkZCTvvfeeWxWwMYZ+/fpRpUoVDh48yE033cT69es5duwYQ4cO5dy5cxw6dChbVVVBOXbsGO3atcv3wUsTEhKc7SeHDx/O6dOnGT16tNs6mZmZ9OrVi/T0dLdSGAfH+V+yZElne8QvvvjC+fp3331HpUqV6NSpE2+++SYdOnRgzZo1zJgxgy+//JJ+/fqxf/9+UlNT+eCDDwDcelbOnDnTmccLFy5QvHhxHnzwQWrXrs3Ro0f55ZdfuO+++3jnnXfo2LEjderUYdKkSdx6662h+Mj85qmqFqxBd2+++WY6depEhw4dKF68OFdffTU1atTINXC/6aabchz/8LfffmPOnDmcPHnSOaC5g81mcxtrc9myZfTv39/jzCE2my2gJjPKMmHCBLcxMTdt2uRxrNN169YhIjRq1Ijdu3fToEED52tVq1YlPj6eWbNmOW8w09LS3GbMOHr0aOjeRBAFEqgdFJE0ABFJAQKa78YYUxIYDbwkIgOAq40xN+S+VfC1aNGCJUuWuA3VEKhGjRqxfft25/OjR486f1DT0tIoVqyY87VOnTrl2nj47Nmzhbbt2vnz57PlvXfv3m53PpmZmc4SpOPHj7tdNIsVK+b1QLP5wTUAHz9+vNcjpxcrVswZiDtKDTp06ECXLl0Aq4rWG1FRUSQlJVG3bl3i4uKc7eGio6NZu3YtVapUAeCpp56ia9eutGnTBpvNRrVq1Th58mS2dkX//Oc/KV26NIcOHaJVq1YcPnyYmTNn8sgjjzBp0iQOHz7sViVbkPbs2cO1116b7yVqEyZMYOPGjYgIJ06cYO3atdmGvVi2bBl33nknTz/9ND/88EO2NBzfvevgxrVr1+bs2bPExcURHx/vLP298cYbqV69unNqpnfeeYe2bduyd+9eDh065ByO5sMPP2THjh3MmDGDkiVLMn78eNauXcu+ffto2LAhzz//PLVq1eLo0aOcPn2aatWqMXDgQGrWrEmxYsV4+OGHc20WYLPZ3K5TBSkiIoJHH33Ubdn7779P9erVOXbsWI5DHTlmjsmqfPnyREZGcvLkSX777Tf+8Q/3MofY2Fhat27N8ePHsdlsztLv3OY8HTZsmO9vTDnNnz/fWYJ58uRJTp065dYWOTU1laSkJD799FPneTR69Gi3Y7h58+Zs27aNbdu28c033wDWrDSO6QRFhKeffjrXfKSkpITFdxmV9yo5usIY8x9gH3AFEOgVvDNW8OcIo5cDtwE5FlUkJyeHZBycChUqBD3dffv28dhjj/HII4+wY8cOatasydGjR7lw4YLbvowxzvF9Dh48mC0f8+bNo3jx4l71Js2JiBRIiV2lSpWYNm0aBw4c4IEHHuDUqVMcOXKE+fPnM3ToULp168a6devo1q0bZcuWZfr06TRu3NiZ11atWpGRkRFWYx8dOnSI1atXU7duXQYOHEjz5s19yp8xJqD3s3HjRjp37szvv//OI488QnR0NCNHjmTz5s289NJLbmmvX7+eAwcOEBkZSb9+/Tw2ek5NTWXTpk3ccMMNzgvlvn37OHnyJPv376dUqVKsWbOmwEsN5s+fT8+ePdmwYYPPPbwDcejQIebPn09SUhKNGzdm4sSJ1KlTx+1znjlzJvfddx9169blzz//dHstNTWVY8eOkZGRgTEGm83G9OnT6dSpE2+88QaZmZm0bds22zHRsWNH57VCRFi1apVzaItp06Zx1VVX8fPPP3Pu3Dl69epFp06d+Pnnn6lcubLzmLTZbEyZMoXu3bs703f8veaaa3I9Ds+ePUvlypXD6tzL6siRI6xdu5ZbbrnFp3xWrFiRokWLsnr1aipVqkRMTAzHjh1j1apVFClShM2bN1OyZEkOHDjA4sWLiY+PZ8OGDdmuz2lpaRw8eJDMzEz+/PNPOnbsWODnSWEjIiQlJVG6dGnmzJnDgQMH+PDDD+ncubPbtfKjjz7irrvuIikpye17cJ21wjFMk6ODyvr165k3bx4RERGsX7+ebdu2ERkZmev1bNu2bSxatMh5Ez1t2jTuu+++fP/9DKRE7RWgMvAkUBHwrYVrdlUB11vTs/ZlbowxTxlj1hlj1oVqHkRHw+1g6t69O61btyYmJsbZ2LV69erZ7sqMMTRr1sz5f1axsbEkJibmuq+82hFNmjTJx9z7b8eOHSxZsoSUlBQaNWrEt99+S1RUFIcPH2bp0qXcf//9xMbGOoeFeOWVV+jYsSNXXnklu3fvztb7MeuwAAWtePHirFu3jhYtWtCsWTO36cXyw7lz56hSpQqlSpWiUaNG1K1bl7Nnz9KyZUuPpcKnTp2iWrVqOX6ORYsW5eTJk9k6UnTu3Jm1a9dSrly5PI+//OAoiXbMI+naiSJUzpw5Q82aNTl//jxHjhzh2muvZe/evc6hVRzDyjh6nDlKAFJSUpzVklu3buXqq692ttPq1q0bP/30E9dccw2bN2/mqaeeon379tn2XbZsWWdVoGP8vbi4OGrWrMm6deu49tprOXDggLPK3HHeuFblRUREcNttt+U5HIcnXbp0oUWLFj5vl9+eeOIJn6vnK1euTP369bn//vu58cYbAdwGtI6NjaV69erOIMLTsD7JycnOXt2NGjXitttuy3GAYZWz7du3M3HiRO68806OHz/O4sWLeeSRR4iPj0dEiI6OJiUlhV27drF//37atGmTbdpAh8jISDIzMzHGOMcodZwXNpuNNWvW0K1bt1x7YO/Zs4eGDRs625yuW7cu3+dfhgBK1ETkHPBfx3NjTCcgkDmQTgCuZ0BZ+7Ks+x0LjAVo37695DZoabjJyMhg0qRJVK9ena5du1K9enWMMTkOvLp+/fpsr61evZrIyEi35Y7xjTIzMxk1ahRbtmzJcZqVzMxM9u3bl+tgr1l50zZMREhNTXU2RN68eTOtWrVi27Zt2Gw2ypUrR7du3RAR3nvvPYYPH061atW48847+fnnn+nRo4dzMFmH+++/36d8FoSmTZsyadIkevXq5fzBzk9t2rThpptu4q677iIqKopmzZrRuXNnZ7CfVcuWLfn73/+e68wGUVFRdO3albi4OBISEpzfwd69e2nTpg3lypUr0B/t8+fP06xZM9q1a8fatWtZv349xYsXz/FYOXHiBBEREdnaHvlq6dKl3HHHHSxdupSiRYvyf//3f2zdupV69erRrl07FixYwF9//UWfPn2cPc82bdrEggULKF68OOXLlyctLY17772XdevWcf78ea6//nquuuoq6tevz/Tp071qmwjWYMqRkZE0bdqUOXPmcM011/DTTz/x9NNPOwNEx9R1roFfuJ9P4cLR1qxt27asXbuW66+/nujoaOrUqUONGjWcx57j81y8eDEVKlSgXr169O7dm8WLF1OtWrUcz0Pl2aZNmxg/fjzFihXjyJEjZGRk8Nhjj3HPPffw+++/s2rVKmJiYnjkkUfYt28f7777Ljt27MjxuP7tt9/o2rUrycnJVKpUiebNm2Oz2di0aRNvvPEGcXFxREZGUq1aNY8D2K9Zs4ZmzZpRqlQpmjdvTsuWLSlatKgzjZxGHwg2n0vUjDHP2v9OdH0AwwPMy0qgnjHG0RCiKzAzwDTDSlRUlFv3+AYNGuQ6jIODowrKMTJ4Vo6GyDNnzuTWW2+lbdu2zsBpypQpbusePnzYp15dEydOZNCgQW7LMjIysnVBnzFjBi+//DKnTp0iPT3d2fYqJSWFm2++mVmzZlG9enUGDhxIREQExYsXp0KFClSpUsXZYyurrONHhaPSpUvz/PPPF0iQBvDYY49Rrlw5ZwlZ6dKlc/1xePDBB2nYsGGuaTraInXv3t1tqIbnnnuOqlWrcuJEtvsnN46pfwBnY/oVK1Y4J8YGnEOKOAYthdx7SjomoU9PT2f48OHOhu8HDx7kvvvuo3Llyh4b7jvy45jcOxAHDhygfv36GGNITEykdOnSzsb8IkKpUqW49dZb3YaT6NKlCzfddBOvvvoqd911l7O0rUOHDs7e3o6qW2+DNLAaSh87dozWrVtz9913A1Z1UNZZUC6VXm/5rVq1am6TyTucPn2a8uXLZ1t/165dtGjRwnl9r1SpUq5t2NRFe/fudf7v2mb78OHD1KlTB7BKlK+77jpee+01Nm3axMMPP0x0dDRVqlRxnkeedOzYkauvvpr69eszd+5cGjduTPPmzUlMTKROnTrUrFmTQ4cO0b9/f/766y+3bR1D/3Tu3JnZs2czf/58rrrqKnbv3s3ixYvzdVQGf6o+k+1/DdZk7I7HpkAyIiLJwDPAl8aYgcAWEfG+K10hcerUKeePekRERK6j4xctWpShQ4fy0UcfAdYk8Y5pWhxsNhubN28GrG7KV1xxBWXLliUpKYkNGza4/TiCVZTry9AjaWlp2QbAHD9+vHNePUcPmri4OF5++WXWr1/Pjh07aNq0qfNCdeWVV7Jw4UK3XlwPPPCA84fL0eBZ+a5OnTo+tZeoVKlSnvPKOr5vR5WQK0egtnr1agYOHOgxAJo3bx7R0dH89NNPfPjhh4BVEux6LA4YMICYmBi3HliO49zBNcj49ddfAatt2l133eUMagYNGkTDhg25+eabmTFjhsf3c/LkSY9DaHjrjz/+AKzqrVKlSpGamupstOw4f3fv3k2TJk2cx7TDlVdeSadOnQDr83RtVhFIO5fbb7+dtLQ0KlSoQIcOHTymV7169Rx7TqrcVa1a1WMvwzNnzjgDtdKlSzurN0WEVq1aOYOMypUrB3TMXYpyGhf0xRdfdH6OrsdwQkKC2+9dnTp1qF27NmPGjKFatWoemwhk9fe//905BM3s2bNp2rQpHTp0cI5FWK1aNZYuXcrjjz/O4cOHWbBggTPYnjhxIo888gjFixfnxRdfdLYFXbBgQY7jAIaKz4GaiDgaOL0tIotFZDFWh4KAGz6JyFwR6SMib4rIu4GmF46ee+45Hn/8ca/Wvemmm3jooYfo2rUr586dy9Z1/Ntvv+XMmTPONmmOOvgqVapw8uRJtm7dmu2H9uDBgzRp0oQLFy74nHfHUBoiQsOGDUlLS3Mr9WrQoAH79u1j06ZNvPjii84pjhwD/rrmvXTp0s67/4YNG1KqVCmf86NCI2tg7qps2bIcOHCAlStX8r///Y8///wzW6mNzWZj+/btnDhxgsaNG5Oenk7RokWdd6jnzp0jOjqaPXv2uFU3rFu3znmRjIuLY8CAAc7xxfbu3YvNZmPv3r0epyqqWrUqxhiPQyacO3fO2a7kgw8+yLXkzpOsc2HeddddPPbYY27LAp1yzlfFixfPVtKd1d/+9rccB69VuStevDjVqlVzGwy3SJEinDhxwjmTSOfOnZ3TtxljaNu2LT179gS0RM2T//znP9lKoRISEujatSuLFy92jv3o8MEHH3hscuMYxsjT3LU5KV++PIcOHaJixYpusxZERUWxbt06WrduzeOPP8758+dZvXo1NpuNIkWKOH+jjDE0bNiQZs2a0a1bN1q2bJntxsjx3hyl/67eeuutbPNW+yLQuT4dkoBHAkjrsuGp2DwntWrVonLlyjRp0sTjvJIzZszg+PHjzgPXwXE3l5mZma1KLiMjg9q1a3P8+HFiYmL44IMPnAM0Ll++PMcGsImJifz3v84midSuXZtNmzaRnp7ubMzpaOSclJREw4YN3YZOeOCBB3LsWeNpvj9VcDp27Jjja8YYevbsyTXXXIMxhptuuonFixe7rVO+fHmOHj3qHMV+9OjRbsOq7Ny5kw4dOjgn/3YM3uoItObMmcPChQt58cUXOXz4MOnp6dSqVYvY2Nhcq/Ieeughvvnmm2zrOHpXpqWlcerUqWwj0ecmMTHReX440q1Tp47bD0q5cuU4cuRInjNZBFteJXJly5bNcz5blbO77rrLOfwJWNfV2NhYZ2lqw4YN3V4vWrSo88ajSJEiXg1GfDmJjIxk48aNJCUl8dlnn5GcnMyCBQt4/PHHOXDggLNU2qFChQq5pudLcxNjTI7XtWrVqjmHaLnlllvYtWsXW7ZsyTZ80RtvvEGlSpWcgyWXL1+eY8eOOQs93n77bc6fP8/EiROJj4/nwIEDzg4mtWvXZu/evR5nfPGGP23UutsHo73eGPO2MeZt4EWgjl85UHlq0qQJP//8c7YG3AkJCezatYtatWqRlpbm/CGpUqWKW7H7qVOnSEpKck4zVKNGDVasWMG8efO45557WLduHT/99BMXLlxwKz1wzHdqjOHIkSMcOnSI2NhYqlWr5pzk/P7773c2bAarVM1RJF2sWDFnNVvWEghX3s69qfLHPffck+vrjkntwerM4Fp96bBp0yY6dOhAq1atSE5OdqvC2LlzJ3feeSfR0dG0b9/e2ei9UaNGLF26lGHDhnHmzBkqVKjAFVdcwZYtW+jYsSOzZs3Ktf1dREQE//rXv5y9mrPeva9YsYInnniCPXv25JhGWloac+fOZcWKFYwePZpt27bRsWPHXHvwVapU6bKd7u1S5rjxdHDUVGR93TGAscpZeno611xzDRs2bOC3337jxhtvZM6cOZw6dYqqVasSERHBjh07gjrQfFb/+9//PC53nZKtaNGipKens3r16jxLyBs1asQbb7zhbAa0YMEC9u/fz2uvvca4ceOYNm0aZcuW5fz584gIDzzwgHPmGF/5U6J2BjgAJAIH7Y89QM4zWKuAlC9fngULFmQLaGrXrs369etp1aqV2/gxlStX5siRIxQvXpzixYvz3Xff8euvvzJ16lSefvppatSowddff83jjz/uLK2Li4tzDq7pKLI/ceIEVatWpXz58uzcuZMHHniAYcOG0alTJ+rUqcPixYv5xz/+wZYtW5zjut16663ONjm33nprrpM1q0uD4852x44dzlKEli1b0qxZM6Kionj99deBiz9sjul/0tPTueqqq5yBXt26dZk2bRq33Xab8wexUaNGLF++nM6dOzNlypQ8h5aoX78+V111FR9//DFjxoxxm0R+586dNG/ePFvJ7oQJE5wjlO/fv5+xY8eyevVqbr75ZoYMGcLNN9/M9u3bc6yer1ixoo6XdYly3KxC9htgsI79cJqxI1zt27ePJk2aEBkZyd69e2nZsqXbrCIVKlRgz549zim+QiGnzjpZS9pEhJSUlDxL7Jo1a0bv3r1JTk4mKSmJOnXqsGzZMpo1a0avXr345z//Sd26ddmxYwdly5YNqE2qP23UNovIV8BTIvKV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fUefPn0oUaIEHTp0YMWKFRhjHO56PnLkCC1btiQuLo6LFy8ye/Zsxo8f73Zz/7Zt21i4cCEvvfQSt912G2B9joYOHUqVKlUQEYYOHerWazirVKlSTs1a3q5dO6fHgkZERNCrVy/Kli3LgAEDaN68OWXKlGHOnDmcOXOGmTNn2udpy8jIYNGiRSxevJi+fftSv359n602Ur58eaZPn87ly5eZO3cuZ86cISUlhYceeojRo0df1Qr066+/MmrUKCZPnszXX39t379161YGDBhw1d+LBx54INfXze38yCrUcpPffHq52bNnD/Xr189zf9b5VatWLaKioujYsSMiQpcuXfjiiy84ffo0f//731m8eDEVKlTg999/B2Dt2rX2z3XTpk3Zvn07s2fPZuDAgV4ZlhIcHEytWrXy3O/p7mtvuHDhgtfH0l28eNErXavh4eHUr1+f+fPnF4mWy4JooVaA9PT0XCfgW7p0KUeOHLlqe2pqKuPHj2f06NGUL1+eChUqFEKWOYWHh3Pw4EG++uor1qxZQ0RERIEXlPr163tkvI4vZWZmMnLkSH744Ydc93/55ZdMnjyZb7/9losXLzJz5kw6d+5sn0Rx+/btNGrUyK0cqlWrxp9//klsbCyVKlXiqaeesheCWWtPZhUfgYGBBAYGsnr1aoYNG+bwexQROnbsyIwZM2jXrh19+vQhNDTUpZtKwBrM+8MPP9CrV69iPSmziFCzZk3atGlDpUqV+O6772jfvj3Tp09n4sSJDB8+nEaNGvHKK694rBXAVY888gi7du3iscceo0ePHtx+++20adOGihUr0rt3bz766COmTJnC6tWrSUxMZOXKlQwaNIiXXnqJ1q1b8+mnn9ovYBEREZw+fZpTp06RkZFBfHy8faC2I66cZgGs2egnTpxI9+7dC7xQJiYm8uabb/LJJ58we/Zs6tatm+tx69ev5+LFi5QtWxawCvOsFqno6GgaNmxIx44dCQoK4vjx43Tt2pW9e/cC1mc8a0xdkyZN2LRpExkZGbm2HLorOTm5wBbNChUq+O20SsYY0tLSCuUzfuHCBZo1a+aV2O3ateP6669n5cqVAFy6dInp06fnuAs9uzNnzvD999/nO+Guz2T13RfF/5o3b268ITMz0/7vMWPGmHfeecfEx8fnOGb48OFm+vTpVz133Lhx5uzZs17Jy1lnz541W7ZsyfF+8rN27VozYsQI89tvv3k5s6tlZGS4HWPmzJnm8OHDZvz48Tne86RJk8z27dvNwoULjTHW7+i9994zSUlJxhhj1q9fb7Zs2WImTZrkdg7nzp0zU6ZMMRMnTjQJCQnGGGOWLFliPv/8czNx4kQTGxub4/jff//ddOvWzcyfP9/s2bPHrF+/3hw+fDjX2ElJSebTTz/NdV9GRoYZO3asw3muXr3apKenm4SEBPPhhx+aU6dOOfxcVXRs377djBw50ly8eDHH9mXLlpnPP//crFq1yhhjzOXLl820adPMRx99lOfnLz9TpkzJ8fizzz4zSUlJZu7cuebMmTM59l15rq9cudLs27cv39jz5883r7zyivn999/tOecnLi7OpKenm8mTJ5tTp06Z2bNn59jfpUsX8+effxYYJ+v1nbF+/Xqzbdu2Ao+bNm2aSUxMdCq2Ny1ZssSMHz/ezJw50/Tt29ds3LjR66/53//+16Snp3v1NT788EPz+++/mw8++MAkJCSYFStW2K8FxhiTmppqli5dakaMGGF27NhhPvjgA3PhwgVjjPVZdfR66Og1DIgxTtY6RbpFLT4+3t607ajMzEwuX75sr56TkpJITU1lzZo1zJgxg/Hjx/Paa69x4sQJNm3aRLNmzRg6dCjjxo1j9erVxMfHM3z4cNq2bXvV0jXbtm2jdu3aXr1t3hmRkZE0a9bM4e6ZW2+9lcGDB7NkyRL7nFSmkJqN+/fvz7x58zh//jxjx461fwty1JYtWyhTpgzR0dG0atWKkSNHsnLlSmJjYwkJCWHYsGH2QbFPPPEEL730kn2tultuuYXFixd75L1GRERQq1YtbrrpJvu39QceeIBLly7x8ssvXzVDd506dRg2bBiPPfaY/SaBHTt28PHHHzNx4kRWrVpl/4a3bNmyPLukAgICCA0NtXc15fZejDH89NNPjB49mvT0dD766CMWLlzIww8/7JOWX+V9jRs3ZtCgQVd1L91///0kJydz++23A9b4rB49etCvXz+io6Pdft3k5GRKlSplb2HObsCAATm27d+/P99uQrAG5999993s2rUr11nur3TdddfZW65XrFhx1ZjDkSNH2sfwFkRESEpKKvDvQ1paGsYYdu3alWOKj7x07NjRq3PQ/ec//2H79u0OH//nn3/y4IMPEhQUxNixY2nVqpXXcsty1113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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "text/plain": " index V model GridEx GridREx CART \\\n0 2016-03-04 00:00:00 410.0 518.750997 456.38289 441.865819 441.865819 \n1 2016-03-04 01:00:00 400.0 522.352090 456.38289 441.865819 441.865819 \n2 2016-03-04 02:00:00 395.0 525.129143 456.38289 441.865819 441.865819 \n3 2016-03-04 03:00:00 408.0 528.767307 456.38289 441.865819 441.865819 \n4 2016-03-04 04:00:00 406.0 528.091871 456.38289 441.865819 441.865819 \n.. ... ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 456.38289 441.865819 441.865819 \n644 2016-03-30 20:00:00 501.0 489.614882 456.38289 441.865819 441.865819 \n645 2016-03-30 21:00:00 518.0 490.609540 456.38289 441.865819 441.865819 \n646 2016-03-30 22:00:00 510.0 491.321955 456.38289 441.865819 441.865819 \n647 2016-03-30 23:00:00 511.0 491.970557 456.38289 441.865819 441.865819 \n\n COSMiK CReEPy \n0 462.073299 477.108816 \n1 460.069181 473.640591 \n2 457.718672 469.171813 \n3 455.242336 465.418572 \n4 454.052186 462.959830 \n.. ... ... \n643 451.642998 432.796454 \n644 452.503446 433.519832 \n645 452.375362 432.264649 \n646 452.801142 431.704527 \n647 454.179640 433.203885 \n\n[648 rows x 8 columns]", + "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.750997456.38289441.865819441.865819462.073299477.108816
12016-03-04 01:00:00400.0522.352090456.38289441.865819441.865819460.069181473.640591
22016-03-04 02:00:00395.0525.129143456.38289441.865819441.865819457.718672469.171813
32016-03-04 03:00:00408.0528.767307456.38289441.865819441.865819455.242336465.418572
42016-03-04 04:00:00406.0528.091871456.38289441.865819441.865819454.052186462.959830
...........................
6432016-03-30 19:00:00497.0487.521085456.38289441.865819441.865819451.642998432.796454
6442016-03-30 20:00:00501.0489.614882456.38289441.865819441.865819452.503446433.519832
6452016-03-30 21:00:00518.0490.609540456.38289441.865819441.865819452.375362432.264649
6462016-03-30 22:00:00510.0491.321955456.38289441.865819441.865819452.801142431.704527
6472016-03-30 23:00:00511.0491.970557456.38289441.865819441.865819454.179640433.203885
\n

648 rows × 8 columns

\n
" + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ "p = pd.DataFrame(predicted)\n", "for e in extractors:\n", - " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())" + " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())\n", + "\n", + "plot(2491, p['index'], p.model, p.COSMiK)\n", + "plot(2491, p['index'], p.model, p.CReEPy)\n", + "plot(2491, p['index'], p.model, p.GridEx)\n", + "\n", + "p" ], "metadata": { "collapsed": false, @@ -326,30 +290,12 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 39, "outputs": [], "source": [ - "def plot(testB, x, pred, extracted):\n", - " missing = pd.read_csv(\"data/missing.csv\", parse_dates = [0], index_col = 0)\n", - " df = pd.read_csv(\"data/averaged1.csv\", parse_dates = [0], index_col = 0)\n", - " df.LPFnorm[missing.index] = np.nan\n", - "\n", - " for i, b in bartels.iterrows():\n", - " if b.n != testB:\n", - " continue\n", - " xminmax = [b.t0, b.t1]\n", - "\n", - " f, axes = plt.subplots(3, figsize=(10, 8)) # l x h\n", - "\n", - " myplot(axes[0], [3, 1], \"LPF\", df.index, df.LPFnorm, xminmax)\n", - " myplot(axes[0], [3, 1], \"\", missing.index, missing.LPF0, xminmax, color='r', marker='*', lw=0, size=10)\n", - " myplot(axes[1], [3, 2], \"V\", df.index, df.V, xminmax)\n", - " myplot(axes[1], [3, 2], \"\", x, pred, xminmax, color='b', marker='.', lw=0)\n", - " myplot(axes[1], [3, 2], \"\", x, extracted, xminmax, color='r', marker='.', lw=0)\n", - " myplot(axes[2], [3, 3], \"B\", df.index, df.B, xminmax)\n", - " plt.subplots_adjust(hspace=0.6)\n", - " plt.savefig(f\"plot/{b.n}.jpg\", dpi=96 * 2)\n", - " plt.show()" + "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", + "pd.DataFrame(rules).to_csv('results/rules1.csv')\n", + "pd.DataFrame(missed).to_csv('results/missed1.csv')" ], "metadata": { "collapsed": false, @@ -360,22 +306,9 @@ }, { "cell_type": "code", - "execution_count": 37, - "outputs": [ - { - "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmUAAAHoCAYAAAAISZi8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAC8/klEQVR4nOzdd3iTVfvA8e/pYG8om7JEioAI4kYRwYELnOB4X30dIODg97r1VUFF2SAbRGQoLlQEFFRky95lbyi0tEDLKB20zf37I8MkTdukTdoU7s915Wr7JDnPSZrnyf2ccR8jIiillFJKqaIVUtQVUEoppZRSGpQppZRSSgUFDcqUUkoppYKABmVKKaWUUkFAgzKllFJKqSCgQZlSSimlVBAokqDMGBNmjHnXGDOpKPavlFJKKRVsiqqlrCywwL5/Y0w3Y8yLxphBxpjSRVQnpZRSSqkiUyRBmYicAU45bXpcRMYA64EHiqJOSimllFJFKayoK2BTyvbzBHC9+53GmB5AD4CyZcteHRUVVYhVU0oppZTKnw0bNpwUkQhvHhssQVma7WcEcMT9ThGZBEwCaNu2raxfv74Qq6aUUkoplT/GmMPePraoBvoboBvQ1BjTBphpjHkRaAv8XBR1UkoppZQqSkXSUibWVdAH2W4AG4uiHkoppZRSwULzlCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDMqWUUkqpIKBBWRBauHAht956a1FXQymllFKF6KILylasWEGtWrX4448/HNv279/PTTfdxAcffABA//79GT9+POPHj+eJJ55wef61117L3Llzs5UbHx/P/fffT8+ePZk4cSJPPvkk8fHxvPnmm9x6661MnjyZyZMn89RTT7k8z37/559/zssvv8x3332X52vo1KmT4/epU6eyb98+j487dOgQTz/9tOPv//3vf3mW7cnRo0e5//77ueeeexzbRITrrruOnj17kpyc7HVZQ4YM4cyZMwAaWCqllFI+CJbksX7Trl07KlasyB133OHY1rhxY5o0aULnzp0B+Pnnn1m8eDGVK1fm5ptvdjxu8eLFPPnkkwwfPpz77rvPpdwaNWrQpk0boqKi6N69OwcOHGDNmjV07tyZM2fO8NxzzzFw4ECmTZvm8jz7/c8//zxbt27l008/pVu3bkyfPp2UlBROnTpFhw4duPHGG/nkk08QEWrXrg3AhQsXWLZsGQD169fntddeo02bNuzevZtPPvmEZcuWsXfvXqZMmcJ1113HDz/8wMcff8z27duZPn06TZo04ejRo7z11lt0796diIgIIiMj2b9/P1OnTnXUsW7durRp04Zjx44xf/58OnfuzHfffUdUVBQdOnSgXLlyjBkzBmMM8fHxdOzYkZIlS9K9e3fee+89Fi1axKOPPsrNN9/M8uXLueaaawgJCSEuLo7Jkyfz+OOPM2TIEOrWrcvu3bt56qmniImJoW/fvvTt25cpU6YwfPhwFixYQPXq1SldujTPP/+8Xz8XSimlVLC76IIyb0yYMIHXX3+dpKQkOnToQIsWLQBYsGABgwYNYt68eWzYsIHWrVvz8ssvExoaymeffQbA0qVLSU5Opnr16txzzz0sX76c6OhoRo4cyd69ez3ub/fu3YwbN45ff/2Vzz//HIAmTZqwcuVKSpYsyfTp02nZsiXz5s1j5cqVJCcnM23aNEqUKMEtt9ziKKdu3bpYLBaWLVvG8ePHueWWW1i0aBHPPPMMALVq1QLgo48+on///jRt2pRu3bpx6NAhunbtyrlz53jppZdo3769x3oOGDCALl26cMMNN3Dy5EkaNmwIwPnz5/n2229ZsWIFKSkpdOzYkVWrVtGgQQNHMDZkyBC6dOlCmzZtALjllluoVasWzz33HLt27WLXrl188MEH7Ny5k/79+/Ptt99Srlw5evbsydNPP82ff/7J/v37uf3222nZsmVB/8VKKaVUsXPRB2V79+6lQYMG2bZPnjwZEeHOO++kXbt2lCxZkpMnTzJ16lSaN2/O0KFD+eabbxgzZozL89q3b0/37t1dtrVs2ZK+ffvmWIemTZvSu3dvUlNTmTt3Lj179qRXr16sXbuWmJgYPvroozxfx7x58zh9+jSvv/46ixYtIi0tDevCCGCxWBy/21nz8+KyvXz58tm2OatevToPP/wwjz76KD/99BNDhw51lOWpPHuZp06dIiMjI1t5xhhEBIvF4vH55cqVwxhDqVKluO6667jmmmuYOnUqX3/9NZMmTcrzPVFKKaUuJhddULZixQpOnz7tCChWr17N22+/zd69e5k/fz7XX389kydPZsuWLWRmZlKvXj1q1KjBiy++yNtvv03btm3ZtWsXN998M7Nnz6Zr166AdUzZxo0biYuLo0OHDtSoUQOA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VWP58uXExsbSqFEjJkyYAMALL7xA9+7dqVevHjExMaxbt462bdtSvnx5Nm7cSOXKlXOsT3h4uMtA9uHDh3PkyBE+/vhjwNr1+ddff2GMYd++fTRu3NhRxpo1a1iyZAl79+71ODjXLj09nbZt27J582bHtvj4eE6ePElERAQXLlygQ4cOrFixgg8//JCff/6Zl156iaVLl3rzFjscOHCAwYMH06ZNG+rWrUv58uXZv38/1atXp1q1amRlZXHnnXcycuRIoqOjueqqq8jKysIYQ2pqquNE/9prr9GgQQNH92/16tWpW7cuMTEx7Nmzh6eeeoqjR4+SkpLC7NmzHf9D5/fX/tpat27NSy+9xHPPPUeDBg14+umnadWqFU899ZTj8RqYqcJ2/vx5ypYtS506dYiPj6ds2bKcP38esI7BjI+P93qc5759+1iyZAmZmZmEh4fzyiuvEBERQb169TzmJqtWrZrLONvy5ctzzTXXuGTwr1WrFnXr1nV5Xtu2bbn22msdf4uIy4XeyZMnqVatWp71HTFiBN988w0nTpzIcViFunT4kjw2S0SGicijInK3iHQRkR4iEpgRmcrvTp48SdWqVTHGsGHDBgDHiU9EWLFihSP/zr333kuPHj3IyspyBGbdu3fn/vvvp0+fPpQsWZL27duzY8cOjh07Ru3atR2Pq1SpEqGhoVSsWJGzZ886lijp1q0bGzZs4KGHHnLUyVMAULduXbZs2UJqaio1a9Zk9+7dpKWlERcXx7x589i6dSvGGNLT02ncuLHjeWfPniUzM5P69etn6wJNSUlxnDCTkpK4/fbbXVrf3n//fU6cOEHjxo05e/YsJUqU4NixY3Tt2pXx48fz0EMPsWHDBo+zTHOydu1a3njjDa6++mrAetW8bt06atSoQadOnbjnnnuIioqiXbt2zJkzh1atWjlaCa+77joee+wxAK655hrHe2tXr149jh49yv79+2nUqBFVq1bllVde4c8//6RWrVrAPwGWMYY9e/Zw+eWXO7aHhITQtWtX6tSpA8Cdd94J/DOrTKnCdOTIESIjI6lXrx41a9akWbNm7Nq1y3E8v/766yxfvjzPcpKSkrjttttISEigWbNmLvd17NiR48ePZ3uOMYbjx487WuiefPJJbrnlFp9fw80338yyZcscf69du5arr77a44Xwzp07uXDhAgsWLOC2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7sS9td2+7qKdPZs8WINvzX9mVadOnTwnaNh7Bt5//31q1KjBiRMnaNasGVdcccUlEZAB+Q7IwDrm1fn7olatWvTv358rr7yShx9+mCVLlrg8fuLEiS7dv8ePHy/UtWt//vnnoDw+pk+f7rI4fKDs378/x+Xr8puE29fuy8+Aj0TkGRH5D/BOvvaa3U/ANcaYF4Gvc3vguXPnHCdMsLYaLV26FLCeXDMyMkhJSSE6OprGjRvTrFkz4uLiWLNmDSNGjHAMVhURIiIiOHnyJG+++SZHjx51yfnl7ueff+b111/n6aefZvr06Zw8edLxhWSf/eisZMmSpKenk5qa6tXyNnYlSpTgyJEj2bo3nV122WUuubvat29/yZzwgkmJEiUuyZmt0dHRXH755SQlJVGhQgVKlixJ5cqVeeSRRwDrlbx9hvCUKVP473//63hurVq1qF69OqGhoaSkpNC/f38sFgubNm3KFizZzZ8/n99++41GjRpx8OBBbrrpJubPn++yDundd9+d7Xn2hemdvzRmzJjBpk2bXCYL2FfjsAd0VatWdYz1XLZsWbbALticP3+ed955h4ULF2b70vbGokWLHF8g9nOps2PHjpGZmUmdOnU4fPgwv/76K3FxcR7LsndfhoSEONZn7dChA3fccYfP9boUlStXzmX8WHh4uKNl0hjDtddey88//8xnn32GiFCxYkWXC5Tjx487xnYWhjlz5uT4WShKxpiA5EJ0FxsbS8OGDbNNzvv222/p379/vsr0KSgT61nrGqdNnfO11+zlpojIGyIyJrdB/mC9Snc+cRw5coRTp06xePFihgwZwssvv8zOnTtJTk7mueeeo2vXrlgsFqpXr86YMWMIDw93ZGS3n3yvvfZaXnrpJTZt2sT69euzLW1RtWpVYmJiKFOmDBUqVKB69eps3ryZFi1aULNmTb+3Uh07dizHNdrAejV1//33O/5+++23/bp/pdyFh4ezfft2PvjgA/766y86dOhAQkICkZGR1K5dm//85z+O4yAqKoro6GjHBUnZsmUd5TRp0oTWrVvz8MMP06tXL1q2bMl3333n6NJ0t2HDBi5cuMD3339PZGQkVatWpUyZMowePZodO3Y4voBeeuklwDq+zDnlyUMPPcTMmTMB6/iPunXrcvDgQaZOncqJEydc9pWYmEjVqlVdgrKTJ0+6rKIRjObM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AQ3KlFJKKaWCgAZlSimllFJBQIMypZRSSqkgEBboHRhjWgMvAauAKsAhIAKoB/QDjO3nESBeRH4IdJ2UUkoppYKNEZHA7sCYSsA44ADQATgpIl2MMY8A4Vhb69JF5AdjzGwR6eqhjB5AD4CyZcteHRUVFdA6K6WUUkr5w4YNG06KSIQ3jw14SxlwHzAX+B5YBrxq234CuB5rS9kq27bSngoQkUnAJIC2bdvK+vXrA1lfpZRSSim/MMYc9vaxhTGmrBqQJCJZwOtAmm17BNYuyxjb7wCphVAfpZRSSqmgUxgtZd8BbxljmgDRwBpjzIu4jSkzxtQAvi6E+iillFJKBZ2AB2UiEgu8nMfD3gh0PZRSSimlgpmmxFBKKaWUCgIalCmllFJKBQENypRSSimlgoAGZUoppZRSQUCDshwkJiZy//33s2TJkoDva/LkyTz99NMB349SSimlgtdFF5S98cYbdO7cmaSkJLZu3cp1110HwObNm+nevTsnTpzgww8/zLOcKlWq0KZNm0BXF4BOnToVyn6UUkopFbwKI09ZoXr//fe56667qFy5MtOnT6dKlSrExMQQEhJCz5492b9/P3PmzOH999/nhRdeICEhgeuuu46VK1fy888/ExMTwwcffMBNN93Epk2buPXWW13K79evH3Xr1mXz5s288cYb9OvXj/T0dG6++WZWrVrF2LFjOXz4MLNmzaJBgwZs27aNQYMGMWvWLE6cOAGAMYbevXvzf//3f9SrV4+srKwieKeUUkopFUwuupaycuXKUb16dQ4cOEBmZibdu3dn1qxZrFixgltuuYXrr7+ecuXKAdC9e3caNmzIm2++SdmyZYmLi2Ps2LF0796d559/niZNmmQr/8CBA5w5c4ZevXpRq1YtbrnlFm688UZeeOEFWrduzffff89HH31EmTJlEBFSU1OJi4ujX79+lC1blrJlyxIdHc2OHTuIi4vjv//9L507dy7st0kppZRSQeaiaykDeOihh+jfvz+PP/441113HQ8++CAPPvggoaGh2R5bvnx5AEqUKEFGRkaeZQ8dOpRDhw7x2muv8c4777jcZ1/c3RhDp06daN26NU2aNKFq1aoA/Pvf/yYkJIR69eoV9CUqpZRS6iJzUQZl9913H2+88QZffPEFYWFhlC5dmiuuuAKA1atXExcXx+rVq1myZAkbN25k7969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eTInTpww+by4f/8+Q4cOZePGjfTq1euBxzdu3LjAG2128oqjoyPnz5/X3zRz8thjj2nSJeDKlSv5GuPFldTUVL0HNqeBHRgYyLFjx4x+JiMjg5kzZzJlyhQ6duyIg4MD5cqV45tvvmHEiBF06dKFgIAAGjZsyBdffMG4ceM0e0Av6CGgfv36nDlzxuIxQJdYExUVRalSpXjsscdYvXo1TZo00e9v164dW7Zseeg7bZiLMspszM2bN/ONDQkODubAAW17vm/atIlu3brRtGlT+vbty/Tp07l//z6bNm2ia9eugC7oesuWLUydOpXmzZszcuRIxo4dy7179/LUtPnxxx/1nzOGq6urPgC+dOnSDBgwgKFDh+Lh4YGbmxvvvPMOLVq0ICQkpFD/j2bNmtGpUyeOHTvGzp07jR6TlpbGihUrjLr/c9+swsPD6dq1KxcuXAAKt9ySk379+tGsWTN9PODhw4cZOXIk27Zto1+/fkVaYsASpJT6uEYpZaEuoFJKZs2axTPPPMOhQ4c4efIkf/75JwEBAfTt27fAz2bHXRmLK7p165bZRlmpUqUYOHAgJUuWNLo/KSmJ1atXm5URmk22J8/d3T3fxJBs/P39efvtt0lNTWXp0qUmyf/777955ZVXTC4b0bJlS37//fd896empupj3hwdHalWrVqeY/z8/IiMjLTYi5KcnIy7u7tFMuyNI0eO6GvC5cxqbNGiBbt37yYhISHPZ+bOncvgwYOZOHGiPnO2c+fOpKSkGJybzs7OfPzxx5QuXZru3btr4i2LiYkxGioDuvp/WpWr2LNnDy+99BIAlStX5t69e7i6uur3CyEYPnw406dPL5axg9ZGGWU25v79+/lmWtapU4dz587pvQ1acP36df2yR7ly5RgwYADz588nOjpav7wghOC9995j4MCBBksOL730EocPH9ZP3piYGIQQeHp6WqRTlSpV9B6SbO7fv2/Ue7Zu3ToaNmyof//hhx9y8eJFAH7//XcWLlzIpEmTOHfuHDNmzKBVq1Zs3bqVmTNnkl14OCYmho8//tjgInTy5EmefPJJ/c00MTHRrKBygFatWrF//35A99RYvnx5wsLCaNy4sdUKNVqDatWqcenSJbZs2cKCBQtM/tzevXtp3749vr6+vPnmmxw/fpyffvqJVq1a6YP886NChQpERERQt27dPMtmd+7csfhcM8aFCxeYO3cuNWvWNDkeyBg5kxhMwcXFhebNm1OmTBkiIyMfeHxoaCjNmjUzOXNNCIGDg4NJS9DDhw83Gojv4OBAREQEkydPNmlMa7F582Y2bdpkUx1yc/r0aX2G6NGjR2natCmg+95Hjx7N7NmzDb7769evU7ly5TxdDby8vJgwYUIe+W3atAF0DxQNGjQgJCSE/fv3c/nyZSZPnlxoI+rXX3+lZcuW+e739PTkyy+/tLh476VLl3jssccA3fnz4Ycf5jnGy8uLDz/8sNgnNFgDZZTZOcOHD7foRgG6rLWUlBSuXLmS50nJ19eXoKAgWrRoYbDd0dHR6FNVs2bN9EU/N2/eTL9+/SzSLZsWLVrwxx9/kJ6eztdff82CBQv0dZ6yA1AjIiJwcHDIs6xYqlQpEhISOHr0KJ07d2bcuHHs37+fN998k2rVqtGvXz9GjhzJokWLuHfvHvv27WPIkCEGWXiJiYkGT6qWZJ6VKlWKxMREMjIy9AZ3mzZtGD9+vD6WQkppt9X/pZQIIShdujTXrl0jIiLCoLH3X3/9ZXBD2Lt3L/PmzSMxMZGUlBROnDhhYDj379+fSZMmmfSdurq6cuXKFQIDA7lyxbBk4d27dx/ogTIFPz8/gxvPvn37GDNmDE2bNrUofiouLq5QRlk2vXv35rvvvjMI+DZG7hY5pvCf//xHP1/Xr1+vN/7+/PNPg98jKCgoXxlSSjp16lSkwfdCCP05JqUkMjKSy5cv26T46Llz51i2bBkZGRl5xi9VqhRJSUlcuHDB4Hrp7u5O3759WbVqlX7br7/+SuvWrc3SITg4mNGjR+Pk5MS2bdvo3bs3M2bMKFTYR2xsbL5trgD69OnDBx98wJo1ayz6raWUBvO0VatWRo9zcHAw+J3Npbhn+efGJnXKFP/Pg07I7MDhnPE0UkqWLFmCj48PL7+cpyuVnuPHj1OuXDmWLl2Kh4cHiYmJfPTRR3mOy3kDfRDNmzcnJCSEMmXKkJmZaXKa+4OoWLEi165dY9q0aQwZMoSyZcty8OBBTp06xZkzZzhy5Ag3btwwiNfKplmzZixZsoRatWpRuXJlAIYMGWJwjJOTE2+//TaLFy8mJiaGL774gsWLF9OoUSPg/42w3Cnc5iKEIDQ0VN+LMXsJ19fXl927d3P37l0yMzPp3r27ReNYg5ylJ1xcXBgwYACJiYksWrSIUaNGsXz5cp544gkGDBgAwPnz5+nfvz9Lly5FSslbb71l0fjXr1+nRo0a+sKlt27dYvny5XmyNs2lbdu2rFq1St+SKWdZGjc3N1JSUkhLS2PLli0PXG7NSVxcnN5DUBgcHBx4//33+fbbb5FSUq9ePaPHmfOgEBgYyKJFi/RdJ1asWEHr1q0JCwszWgzZGNOmTeP8+fMcOXKEZ555ptA6mIOXlxfx8fF4e3sTHx9P+fLlqV27Nj/99BOdO3cuEh1At8S7YcMGgoODWbRoEVJKkpOTGTRoEKVKlSIoKIhZs2YZrT2XvcoQHh7O5s2bcXV1LdAoMoWWLVvqvV2vv/46U6ZMISMjgw8//NBoaaVs7t+/b9IDTbaXb86cObRr185oL1ktqVWrFhEREUbjGQvi4sWLHDhwgHv37nH27FnmzZuX5xpeXFFGmQ2Jj483WGvPj06dOrFt2zZeffVVQJclGRgYSGRkJKdPn+bxxx8HdEtly5cvJyEhgfv37xMQEMDOnTv56KOPNDOesift1KlTzcqEK4iIiAiGDh2qlxsUFERgYKA+bqN79+5Gl3pr1aqFo6MjnTp1KlC+m5ubvjBmzombmJhoEO+ixdN4QEAAmzZt4vPPPzfY3rVrVzZs2MDff/9tllelKIiNjdUnbowYMQLQPfk3adKElStX8swzz+Dn58fs2bOpVasWUkpKlizJyJEjNRn/5s2bVKhQQf87/P777/Ts2VNvcFuKm5sbd+/e1Xtlcy7ptGzZkkOHDnHx4kVu375NRkYGTk6mXSbN9ZSBzjB74403mDJlCjVq1MhT3DYtLc2sOSyEYMiQIcybNw9HR0cGDx7Md999x7hx4wolp1atWuzbt09vlF24cIGqVasWaAhkY858ymmURUVFUaVKFRo0aMDRo0e5ceNGgTXatOKXX34hNDSU4cOH4+npqT9PQkNDef/995k8eTLe3t5Gl+eyefXVVwkJCaFXr176kAat8PHx4aOPPiI8PJzff/+d1q1b888//xgt33P48GGaN29uklw3NzfeffddFixYUCijLCUlpdCJV61atWLhwoUMGzaMJUuW4OLiQqVKlWjXrh0lSpRgwYIFJCQk0L9/f71Be+vWLTZu3MioUaMQQnDu3DmmT5+Os7MziYmJjBkzptAeZXtCLV9aAVODFzds2FBgkHw23t7exMXFMXbsWPbv389vv/1G48aN6dy5M/v372fdunXMnz+fiRMn0q5dO4YPH86oUaN48cUX+eCDDzQzyLIRQvD000/nWfK0lOnTp+d5YnJzcyM8PJwaNWrojU9j+owaNcrkcXJ6w6SUHD16VN/c2tPTU5NMp+bNmxMREWH0ht61a1fef/99i8fQmtTUVKKiorh165ZRg7tFixZs27aN1q1b06BBA0aNGsXly5c1r7zu6elpkM0bExOTb4ayufTq1YspU6bQpUsXA09x7dq12bp1K56envTs2ZOff/7ZZJk5A+fNQQjB0KFDmT59ep4K69kxj+aSlJSEn58fPj4+hTbIsnXL+SCzfv16vvrqK5M+GxcXV+gEjexrHqA3ykD3YJZfYo+WXL58matXrzJ+/Pg8cYxNmjShUaNGJhngQgjGjBlDQEAAb7zxhuZ6CiGoV68eZ86c4ejRoyxcuNBouYm//vqrUCsi5pAdO1oYXF1dadiwIYsWLaJ379707duXOnXqMH36dK5du4abmxvDhg1j9erVnD17lps3b7JgwQJGjRqFs7MzTk5OPP7447Rt25Zx48Yxbtw4li1bZqX/YdGgjDKN+eeff/jyyy9NOjY5OdnkoN0KFSowYsQIUlJS9MsuDg4OpKWl4eDgwLBhw5gwYQJ+fn44ODhoEntTEMHBwfkus5hLfjpHRUWZ/JRXGCpXrsyECRM4f/68ftnJkjIQOfH29ubTTz/Nd392PIUt2bNnj0HsyN69e1myZAmHDh3Kt93R8uXLDRIgBg8eTJcuXTTV6/HHH8fJyQlXV1eSk5PJzMzUvO1YxYoVmTBhQh7vmxCC1157jZ49exIQEFDkXTW8vLz0y0c5CQsLo0GDBmbL7dmzZ76xPYXlxo0b+Pv7U6FCBZPa85hToyzbUwaGtRwLemiKiooymvFoKunp6ezatYv58+fr47byw9Ilei0RQuDq6srBgweZMWOGQY2/d999l4yMDDIzMwt9vXF3dy9UGZ9Tp07RoUOHQi/Rtm7dmpEjR1KmTBlcXV2pXr06L7/8MqtXr6Zv3756L/zJkyfZu3cv48aNy+NoyE6yKFmyJE2bNmXJkiXFtuSGWr7UmD179pjUmPrq1asFFn/MTXbsUe44kOHDh+uXELT2iNkL77//Pl5eXprLDQ4OpmbNmgbGpa+vLxEREXn6w5nDg6r8e3h42LQP3JEjR7h48SKDBw8GdA2uJ06cSPfu3fWGf25ye/6sYfy/8847ADRq1MiqLZHyIzAwUP/3E088wbJly+jXr1+R9aP18PCgbdu2rFmzhp49ewK6IH9LvHBaFm7dt28fzz33HNHR0Rw7doygoCCio6OJioqiWbNmgC6GaciQIfTo0YMjR44Uemnby8uLkydPAjpjKee1rVGjRnz11Vc8//zz7Nq1i06dOiGEYP369Xh7e+vLS/Tr169Q5+fatWupX78+rVu3NmlZ1p7IOV8rVapEdHQ0Pj4+pKamsnDhwgLr5uVHgwYNCAsLM7leY2ZmJl26dNEk8L5evXoG12UhhH4uPIinnnqKgIAAvvvuO+7cucPbb79tsT5FiV14yoQQ/kKIrUKIr4UQfWytj7n88ccfeHl54enpSWJiIgkJCaSnpyOlzON92bVrV4H9FE3FxcXF5h4Xa/PEE09YRa6Xl1ceb1/FihVZsWKFwY3ZWjz//PMG2VlFyeXLl/XV3CdOnKjPBBVCsGLFCpvGZGTfgOvXr2/zyt9t27alTZs2rFixgjVr1hTZuIGBgVSvXp2QkBBmzZqlT0ixNVJK7t69q5872QVH9+zZw2+//abPXj1+/DiDBg3Cz8+PsWPHFvrBI6enLDfZPVNPnDjBW2+9xZkzZzh+/Dhjx47lzTffZPjw4bRv356FCxcye/ZsNm7caDSu7ebNmyxevJivv/6aO3fuEBcXR8OGDYudQZabDh068Msvv3DgwAGGDRvGjh07zPKSFqag7N69e6lSpQoODg524Rzw9fVl4MCBlCtXzmqdcQoiv3PXFOzJU3YWuAjkuQoLIQYDg0HbJz6tyG52/NdffzF06FB9tuAff/yBEAJ3d3dKly6Nt7e3vqieOentCuvj4eHBgAEDzHqyLCze3t44OzvnORdSUlJwcnIyOcDcHHbu3Mnrr7+Oi4sL1atXZ+DAgXzwwQcAdlPkM7ujhTW/B1Pw9/fXF7O9cOFCoTPFzKVp06Y0bdpU36PS1pQrV47Y2Fjg/2PMMjIyuHDhAomJiYwdO5YpU6YwcuRIQkNDGTRokNnexQf13HRxcdEvLxrLxqxUqRLDhg0DdO2kli5dauBNunfvHkuWLOHtt9/m+vXrTJo06YHt1IoLHh4eJCQkcPfuXZ599llWrVpl1u+Q8zfIzMxk/fr1vPLKK3m8j/fv3yc8PFyzRB8t6dSpE7NmzWL8+PFGC0dHRkbi4OCQbxLR/v372bNnD59++qnJ3+Hhw4eZP38+K1as4Ny5c/pENVOxC08ZEA1MBBYBU3LvlFIullIGSikD7eHilJsDBw5w6tQpfYmAunXrEh0drQ+0Hz16NG+88QZ3797l8uXL/Pvvv3ZpXCp0N5v27dsX2Xht2rTht99+47PPPuP8+fPcv3+fqVOnGhRVTEhIYMKECaSkpOgr7OdHeHg4X3zxRYFjZlfoz14Oq1mzJtOmTSuw6bqtsMaytTk899xz9OvXj7Vr1xZ5rSx7ueZVrVqVK1euGPz/hw8fztatW8nIyNAHtc+YMYOSJUtqstybkJBgsVFet25dHnvsMX7++WdiY2NZsGABCxcuZOzYsbi4uFC1alWmT5+uSciCPZG9gmJJeIQQgrt37zJ58mSqVq3KpEmT+O233/T7Q0NDWbduHe3atbNYX2tQtmxZRo8ezffff290/+7du9mwYQN37tzh5MmT7N69W79PSklYWBivvfYau3btMqlh+/Lly4mPj6d37976sh0FZecaw148ZbWAi1JKKYSwF51M4vz589SqVcugHIODg4PR+kavvvoq33//PRkZGZoVXVUUbwICAliyZAnNmjXj8OHD/PDDDwwZMoQffvhBf8zPP/9Mhw4dWLp0KSVLlqR69epGi31KKdm2bRvt2rXj119/JTg4OM8xt2/fZsWKFXmWM/IL7Lc1HTt2zFNE1lY4OjrSp08f5syZg7+/f54EB1sUNi1KqlWrxqFDhwy2ZWc+3759G9BlS3/yySeajblhw4YCazGaSlBQEAcPHmT9+vUMHDjQLpbYrElGRoZJ5ZYehJubG7NmzeLdd9/F1dWVZs2aMW/ePH3m/aFDh3Bzc3tg/Kwt8fLyIjU1FSklc+bMITU1VR+3mpqaipubG9999x0NGjTgn3/+wdvbmxMnTlC5cmWCg4OpXbs2v/76K5MnT+aTTz4hKioKLy8vVq1aRXp6OmlpaQwcOBDQfe8dOnQgKSmJb775Bsgbh/sg7MUAqgT0FUJcBTYUdGB2JomDg0OhaghpTVhYGE8++SQbNmzgvffeM+kzjo6OvP7661bWTFGcyF4C6tChg8FFtFWrVmzfvp3k5GTOnz9Pz5499RfCkJAQA6MsO7Pq8OHDBAUF0axZM9auXUtISAgdO3bUZ5Zev36dpUuXMn78+GJzUypTpozZ/S6tgb+/P6NGjSIkJMSg3t3Fixfz7Sv4sODj48PNmzfzxLA6OjpazZuXkJCg2e8fFBRUYOeCh4kqVarg7+9vsZygoCDq169vcG3y8fHh2LFjlCtXDldXV32ikD0TFBTEV199Rbdu3Thz5gznzp3TZ55nG1Sga204YcIE+vfvT1xcnD6Wc9CgQcTExLB27Vri4+P5559/aNeuHS+88AKpqamsXLmStLQ0/f3d3d2dBg0aGO0n+yBEcXu6q1atmuzfvz9lypTBycmJjIwMnn322TwB2/Hx8cTExFCnTh3Nxs7MzCQjI4N9+/bxyy+/4OXlRffu3YssxkTxaLF8+XKefvrpPMuK27dvx9PTk4MHD+Lm5oYQgvj4eDw8PBg9erRBHbbFixfTtm1batSoweTJk3nvvfdsHqP1MHDixAlu377NM888g5SSKVOm8N5771m9FI2tWbhwIUKIIikJsXDhQqSUDB061OpjKUznzp07LF++nMjISPr06WM3SSimkp6eri97U758+ULV25w7dy5CCDp27EhAQIDJSXZCiFAppUnZY8XOKKtcubJct26dQcuVNWvWEBcXh4ODAy4uLiQmJpKZmUnZsmX1FYKbNGmCs7MzSUlJhIeH06RJE5O/0Pj4eHbu3MmFCxeoXLkyvr6+tG/fnrNnzxZJQLhCkZv//e9/DBs2TG+U5UdmZiYzZszA3d2dF154QZOnZ4WO7LIMGzdupHfv3o/Ed1uURtknn3xC48aNefHFF60+lqLw5O6MUpwwV/etW7eSnJxMjx49CvW5h9ooCwwMlMeOHTO6LzU1lfT0dIPilmFhYSQmJnL48GF989gmTZpw4MAB3nrrLXx8fJBSsm/fPmrWrGngboyOjmbdunU4OjrSq1cvypYtW2xPQsWjy+nTp3F3d6d69eq2VuWhIi0tjd27d9OmTRu7yVi1NvPnz8fZ2ZlBgwZZfaxRo0bxzjvv4OfnZ/WxFApTyO4CU1iPeGGMsodqHcPFxSVPgcXsKti5C+DVqVOHX375hR49evD9999Tq1YtVq9eTb169bh69SoZGRmUKVOGkSNHPvRLEoqHm/zaUykso0SJErzwwgu2VqNIcXJyKrIYv+DgYH17JYXCHsjdbswaPFRGWWHw8fEhNjaWffv24evrS/PmzQkICCAqKkrztjEKhULxMFCzZs0i62zQrVu3IhlHobAnHlmjDHQFBK9cuUL//v0BXX/Jhz2DSqFQKMwlODj4oS/9oVDYkkfaKHvrrbfw9PS0tRoKhUJRLFCZuwqFdXmkZ5i9VAtXKBQKhUKhUBHsCoVCoVAoFHaAMsoUCoVCoVAo7ABllCkUCoVCoVDYAcooUygUCoVCobAD7CLQXwjhDkwErgDXpZQ/2lYjhUKhUCgUiqLFXjxlXYGjUsq5wKu2VkahUCgUCoWiqLELTxngB/yR9bdb7p1CiMHA4Ky3qUKI0xqPXw6ItWN5xUVmcdDRGjKLg47WkFkcdLSGzOKgozVkFgcdrSGzOOhoDZnFQUdryLSGjrVNPdBejLJIwCfr7+TcO6WUi4HFAEKIY6Y29jQVrWUWBx2tIbM46GgNmcVBR2vILA46WkNmcdDRGjKLg47WkFkcdLSGzOKgozVkWktHU4+1F6NsAzBRCFEBWGlrZRQKhUKhUCiKGrswyqSUScC7ttZDoVAoFAqFwlbYS6B/YVhcDGQWBx2tIbM46GgNmcVBR2vILA46WkNmcdDRGjKLg47WkFkcdLSGzOKgozVk2lRHIaW0wvgKhUKhUCgUisJQHD1lCoVCoVAoFA8dyih7hBFC/CKEaGlrPRTWQQjhJIT4SAihmTs+t0whRBMhxDEhRBV71dFaMoUQrYQQu+1ZR3uTWRx0LAosnTfFGUvmzaOAXQT650YI0Q74EaghpbwthHACwoFpUsqvLZTtniWrvpQy0XJt88jvAtQBnIHzlnYnEEI0AJoC7kA5KeXHlmsJQoj2gGb/fyGEPzAHuA7sk1KuslCeIzAauAF4ZRUWtlTH/kAr4D7QQErZVAOZjYCR6OrslZFSTrVQXn3gdeBvIEFKucYCcSWBHcDQLNk90ZWe8QMmSinzlJ8prEwgHrCkbmBuHWcDJ4D/AOO00FEI8Ra6UjvPAh9IKaMslZlFC0CYIcuYjl8AFbL2jZFSJmggMxioCZQH1kkpL2ggczNwEygL7JZSzrdQ3gIgDN01c4aUMlIDHT8FLgPVgOmF/S5zX8PROS8smjdGZB7HsnljTGYQFswdI/LKYOG8yed+aMm8MaZnQyyYO0bkXcfCeWNEZl9MnDd2aZRJKfcIIbYD44EPgW7AWeAXIcTHQAK6kzoW+Ab4HmgspXzDBPFdge+A3kIIX6AucBQoDSwFfgLmAx2llC+boX6olHKLEMIzSzeLjDIpZZgQ4h6672KjJbKyEUIIIBAwuXaKiZwFLgKnNJDVGd1FNRndhUYLdgMr0F3E39JI5r+AKzpd2wAWGWVAe+AXKeV2IcQBwGyjTEp5RwhxK8emPlLKF4UQ3YGXgUIbzrllSikjdKeTZjpOlVJGCyGeBGpgxo3LiI6LhBCvAanANS30FEJ0Bn4G2mohD90D0gF051KSRjLHAFuBFHRGihYyR0gpI4UQb2NG+SIj8k4Cvujmzs/oalZaKvMpKeUnWQ9h3YBvCyky9zXc2dJ5k1umlPJHS+ZNPnqOtnDu5Naxm6XzJrdMIUQKFswbYzLR2QKWzJ3c8hywcN4YkWnyvLHn5ctdQAMhRCV0BtNNwBOdlyMO6C2lPA1ckVLOAYaYKLcqMAOd5XoI+E1KOQtokCX3L3STrpc5Skspo7P+fBn4nzkyjMi8iK5kiKn/xwfRFY0MvBxEo+tfugiYooG8OkCMlHIB8JEG8pBSRkspM9H99hZ58nLQGd0E/gT4VAN5y4DaQoh+QCkN5OXENev1Jrp5YHdk3VRKARlZ81srud+hO0e18I46AE9IKcMsVuz/mZelYzo6Q0ILgtDdAK4BfbQQmHVjKQG4SSnvaCCys5RyItAP7R6+5mQZZLX5fw+KyRi5hls8b6x0XzCQaencMaajpfMml8yv0GDeGNHTorljRJ7F88bIb2PyvLFnowxgGjrjIdvb1Aad0bQCcMnalgggpUx9kDAhRB2gIvBS1uefzLFbn4YqpUwwRV4B47yAzmMU/aBjTZD1XLZOgIel8rLwR7eMFwi8KITwKfhwk6gFZEpdOq8WHtjrwN2svzU7T7O8hBWklFc1ElkOiJNS3gfe0UCeBJZKKVcA5iyzFURK1qsPcEVj2ZoghCiNzov5uRCivEYyn8v68xpQSQORtQBnIcQQoLIQoqMGMmtkvd5Ct2yiBZellGnoHjbLaCQToAcWrgDkwDHrtSK6ZXstCJNSfgtcQOe9LzS5ruGazBst7wvGZGoxd3LJ02Te5JDpjEbzJtd3afHcySVPk3lj5Pc2ad7Y5fKlEKI1EIxumbEfun6YtYF/gJ7ojIpaQohqWa8tpZSHHiDTCfgYmCSlDM9aupwErBVCjEa35OaSJa+jlHK7mbq/BLyHLk7CA8sbrPsIIT4EMim8G94oUsoZWTFgz6PzPGrxxFsJ6CuEuIquQ4OlrAOmCCEGofNEacULgFm/bT6sAd4XQtRCm2XbAGCEEOIcuidLs8kyQHui87w1BlYJIUaQFRujkUyBzkjpDUzXQN5cdDGfc9A9kG3SQGa/rPneEHi7sPJyywRKSik/FUJ0RWdEx2mg42AhxJ/oPPafW6pjlszZWcslvpjpncktU0p5HKgtpfxeIx13CiGGofPEzNBI5gAhxCmgMjrPc2HlvYThNVyLeWMgUwjxFRbMm3z0DMCCuWNEHhrMGwOZUspXLZk3+eh5z5K5Y0SeFvMmt8xXMXHePNJ1yrKMP/+spyqFQqFQKBQKm2Hvy5fWpmXWP4VCoVAoFAqb8kh7yhQKhUKhUCjshWLnKRNCPCOEMCt404isYld0UKFQKBQKxcOJXQb650TkKp4KLEe77BVjBSEVCoVCoVAoihy795Rl1TTZh65u1UEpZYSGsu+gS6NVKBQKhUKhsCl2b5SBVYqnKhQKhUKhUNgVdm+UWal4qkKhUCgUCoVdYfdGGVnFU4UQ7wPfZtUWqyR0zZUtwkjRQYVCoVAoFAqboEpiKBQKhUKhUNgBxcFTplAoFAqFQvHQo4wyhUKhUCgUCjtAGWUKhUKhUCgUdoAyyhQKhUKhUCjsAGWUKRQKhUKhUNgByihTKBQKhUKhsAOUUaZQKBQKhUKRCyHESiHEGCHE31mvF4QQFaw6pqpTplAoFAqFQmGIEMJfSnlJCLFHStlOCLEMWA48DrQBwgBnIB54TEr5lhCiM1Al6983WW0iTUZ5yhQKhUKhUChyIaW8lGvTgazXn4CLUsrPgMellDMAl6x9o4BkIA6d8VYonMxTVaFQKBQKheKRJTHr9Y6RfcvR9equWlihyihTKBQKhUKhMIIQog26ftu1gBZAZcAXaJzVM7tWjtfmwGzgIyATWFLo8VRMmUKhUCgUCoXtUTFlCoVCoVAoFHaAMsoUCoVCoVAo7ABllCkUCoVCoVDYAcooUygUCoVCobADlFGmUCgUCoVCYQcoo0yhUCgUCoXCDlBGmUKhUCgUCoUdoIwyhUKhUCgUCjvg/wB7Kvfl9dheigAAAABJRU5ErkJggg==\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "plot(2492, p['index'], p.model, p.GridREx)" - ], + "execution_count": 21, + "outputs": [], + "source": [], "metadata": { "collapsed": false, "pycharm": { From 707b79d4fc2c12ae39826bd9c771524984be1273 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Tue, 20 Jun 2023 23:36:01 +0200 Subject: [PATCH 61/67] wip --- demo/DemoRegressionScaled.ipynb | 209 +++++++++++++++++--------------- 1 file changed, 112 insertions(+), 97 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index 3c90fa85..cf02cb48 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 38, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 39, "outputs": [], "source": [ "def plot(testB, x, pred, extracted):\n", @@ -58,7 +58,65 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 75, + "outputs": [], + "source": [ + "def splitTest(data):\n", + " b = bartels[(bartels.n == 2490)]\n", + " data = data[(data.index >= b.t1.values[0])]\n", + "\n", + " b = bartels[(bartels.n == 2495) | (bartels.n == 2501) | (bartels.n == 2506)]\n", + " idx = np.zeros_like(data.index, dtype='bool')\n", + " for _, row in b.iterrows():\n", + " t0, t1 = row.t0, row.t1\n", + " idx = idx | (data.index >= t0) & (data.index < t1)\n", + " return data[~idx], data[idx]\n", + "\n", + "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", + "data = pd.read_csv(f'data/halffuzzycoefs4B3.csv', parse_dates=[0], index_col=0)\n", + "train, test = splitTest(data)\n", + "train.to_csv('data/alltrain.csv')\n", + "test.to_csv('data/alltest.csv')\n", + "\n", + "data = pd.read_csv(f'data/pruned.csv', parse_dates=[0], index_col=0)\n", + "train, test = splitTest(data)\n", + "train.to_csv('data/prunedtrain.csv')\n", + "test.to_csv('data/prunedtest.csv')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 73, + "outputs": [ + { + "data": { + "text/plain": " Bmin Bmedian Bmax Btrend Bhalf1 Bhalf2 \\\n2016-03-04 00:00:00 4.0 7.5 12.3 -0.080751 0.009474 -0.106862 \n2016-03-04 01:00:00 4.0 7.5 12.3 -0.086067 -0.012065 -0.101174 \n2016-03-04 02:00:00 4.0 7.5 12.3 -0.090328 -0.029534 -0.094717 \n2016-03-04 03:00:00 4.0 7.5 12.3 -0.094527 -0.046781 -0.089474 \n2016-03-04 04:00:00 4.0 7.5 12.3 -0.098312 -0.062166 -0.087105 \n... ... ... ... ... ... ... \n2017-07-02 19:00:00 1.6 8.0 18.2 0.168533 0.139028 0.004049 \n2017-07-02 20:00:00 1.6 8.3 18.2 0.165550 0.157085 -0.019089 \n2017-07-02 21:00:00 1.6 8.3 18.2 0.161067 0.174393 -0.041862 \n2017-07-02 22:00:00 1.6 8.5 18.2 0.156652 0.188077 -0.061781 \n2017-07-02 23:00:00 1.6 8.5 18.2 0.151061 0.198158 -0.086761 \n\n Bthird1 Bthird2 Bthird3 GCRmin ... GCRhalf1 \\\n2016-03-04 00:00:00 -0.001462 -0.016066 -0.068720 -3.883201 ... -0.034045 \n2016-03-04 01:00:00 -0.004731 -0.012913 -0.074356 -3.883201 ... -0.035872 \n2016-03-04 02:00:00 -0.007107 -0.008399 -0.080310 -3.883201 ... -0.038517 \n2016-03-04 03:00:00 -0.010202 -0.001853 -0.085397 -3.883201 ... -0.040322 \n2016-03-04 04:00:00 -0.012979 0.006375 -0.089585 -3.883201 ... -0.042501 \n... ... ... ... ... ... ... \n2017-07-02 19:00:00 -0.032341 0.005700 0.162350 -4.670171 ... -0.059403 \n2017-07-02 20:00:00 -0.032566 0.005048 0.159932 -4.670171 ... -0.058878 \n2017-07-02 21:00:00 -0.033405 0.002897 0.157075 -4.670171 ... -0.058062 \n2017-07-02 22:00:00 -0.033445 0.001775 0.153994 -4.670171 ... -0.057460 \n2017-07-02 23:00:00 -0.033619 0.001199 0.149502 -4.670171 ... -0.056252 \n\n GCRhalf2 GCRthird1 GCRthird2 GCRthird3 GCRfourth1 \\\n2016-03-04 00:00:00 0.060357 0.002682 -0.072897 0.065471 0.007162 \n2016-03-04 01:00:00 0.061504 0.000701 -0.070397 0.064501 0.006544 \n2016-03-04 02:00:00 0.062143 -0.002516 -0.066904 0.064396 0.004833 \n2016-03-04 03:00:00 0.063626 -0.002422 -0.062109 0.066305 0.004309 \n2016-03-04 04:00:00 0.064246 -0.005347 -0.056971 0.065904 0.002981 \n... ... ... ... ... ... \n2017-07-02 19:00:00 0.016938 -0.075540 0.018295 -0.025042 -0.074034 \n2017-07-02 20:00:00 0.015490 -0.076592 0.020250 -0.027076 -0.076765 \n2017-07-02 21:00:00 0.014373 -0.076522 0.022473 -0.028479 -0.079294 \n2017-07-02 22:00:00 0.013574 -0.077431 0.025158 -0.029432 -0.080745 \n2017-07-02 23:00:00 0.012860 -0.077216 0.027493 -0.031331 -0.082300 \n\n GCRfourth2 GCRfourth3 GCRfourth4 V \n2016-03-04 00:00:00 -0.092158 0.046471 0.069499 410.0 \n2016-03-04 01:00:00 -0.095190 0.049612 0.069178 400.0 \n2016-03-04 02:00:00 -0.098934 0.049670 0.071374 395.0 \n2016-03-04 03:00:00 -0.102389 0.052382 0.072217 408.0 \n2016-03-04 04:00:00 -0.105296 0.052220 0.070273 406.0 \n... ... ... ... ... \n2017-07-02 19:00:00 -0.027912 0.071203 -0.054068 477.0 \n2017-07-02 20:00:00 -0.026023 0.071905 -0.053493 442.0 \n2017-07-02 21:00:00 -0.022508 0.073575 -0.054818 437.0 \n2017-07-02 22:00:00 -0.020645 0.074790 -0.054839 436.0 \n2017-07-02 23:00:00 -0.017545 0.074579 -0.054656 430.0 \n\n[9720 rows x 23 columns]", + "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 \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
BminBmedianBmaxBtrendBhalf1Bhalf2Bthird1Bthird2Bthird3GCRmin...GCRhalf1GCRhalf2GCRthird1GCRthird2GCRthird3GCRfourth1GCRfourth2GCRfourth3GCRfourth4V
2016-03-04 00:00:004.07.512.3-0.0807510.009474-0.106862-0.001462-0.016066-0.068720-3.883201...-0.0340450.0603570.002682-0.0728970.0654710.007162-0.0921580.0464710.069499410.0
2016-03-04 01:00:004.07.512.3-0.086067-0.012065-0.101174-0.004731-0.012913-0.074356-3.883201...-0.0358720.0615040.000701-0.0703970.0645010.006544-0.0951900.0496120.069178400.0
2016-03-04 02:00:004.07.512.3-0.090328-0.029534-0.094717-0.007107-0.008399-0.080310-3.883201...-0.0385170.062143-0.002516-0.0669040.0643960.004833-0.0989340.0496700.071374395.0
2016-03-04 03:00:004.07.512.3-0.094527-0.046781-0.089474-0.010202-0.001853-0.085397-3.883201...-0.0403220.063626-0.002422-0.0621090.0663050.004309-0.1023890.0523820.072217408.0
2016-03-04 04:00:004.07.512.3-0.098312-0.062166-0.087105-0.0129790.006375-0.089585-3.883201...-0.0425010.064246-0.005347-0.0569710.0659040.002981-0.1052960.0522200.070273406.0
..................................................................
2017-07-02 19:00:001.68.018.20.1685330.1390280.004049-0.0323410.0057000.162350-4.670171...-0.0594030.016938-0.0755400.018295-0.025042-0.074034-0.0279120.071203-0.054068477.0
2017-07-02 20:00:001.68.318.20.1655500.157085-0.019089-0.0325660.0050480.159932-4.670171...-0.0588780.015490-0.0765920.020250-0.027076-0.076765-0.0260230.071905-0.053493442.0
2017-07-02 21:00:001.68.318.20.1610670.174393-0.041862-0.0334050.0028970.157075-4.670171...-0.0580620.014373-0.0765220.022473-0.028479-0.079294-0.0225080.073575-0.054818437.0
2017-07-02 22:00:001.68.518.20.1566520.188077-0.061781-0.0334450.0017750.153994-4.670171...-0.0574600.013574-0.0774310.025158-0.029432-0.080745-0.0206450.074790-0.054839436.0
2017-07-02 23:00:001.68.518.20.1510610.198158-0.086761-0.0336190.0011990.149502-4.670171...-0.0562520.012860-0.0772160.027493-0.031331-0.082300-0.0175450.074579-0.054656430.0
\n

9720 rows × 23 columns

\n
" + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.read_csv('data/alltrain.csv', parse_dates=[0], index_col=[0])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, "outputs": [], "source": [ "def getTrainTest(data, testB):\n", @@ -82,7 +140,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 4, "outputs": [], "source": [ "def gridex(model, train, test, normalization):\n", @@ -100,20 +158,26 @@ " return gridREx.brute_predict(test, 'default'), gridREx.n_rules, sum([p is None for p in gridREx.predict(test)])\n", "\n", "def cart(model, train, test, normalization):\n", - " CART = Extractor.cart(model, max_depth=5, max_leaves=7, normalization=normalization)\n", + " CART = Extractor.cart(model, max_depth=10, max_leaves=10, normalization=normalization)\n", " CART.extract(train)\n", " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", "def cosmik(model, train, test, normalization):\n", - " COSMiK = Extractor.cosmik(model, max_components=10, k=150, patience=10, close_to_center=True,\n", + " COSMiK = Extractor.cosmik(model, max_components=10, k=125, patience=12, close_to_center=True,\n", " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test, 'default'), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", "\n", - "def creepy(model, train, test, normalization):\n", + "def cream(model, train, test, normalization):\n", " CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n", " output=Target.REGRESSION, normalization=normalization)\n", " CReEPy.extract(train)\n", + " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])\n", + "\n", + "def exact(model, train, test, normalization):\n", + " CReEPy = Extractor.creepy(model, clustering=Clustering.exact, depth=5, error_threshold=.5, gauss_components=10,\n", + " output=Target.REGRESSION, normalization=normalization)\n", + " CReEPy.extract(train)\n", " return CReEPy.brute_predict(test), CReEPy.n_rules, sum([p is None for p in CReEPy.predict(test)])" ], "metadata": { @@ -125,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 7, "outputs": [ { "name": "stdout", @@ -136,39 +200,29 @@ "GridREx\n", "CART\n", "COSMiK\n", - "CReEPy\n" - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/3576493649.py\u001B[0m in \u001B[0;36m\u001B[1;34m\u001B[0m\n\u001B[0;32m 34\u001B[0m \u001B[1;31m#if name in ['GridREx', 'CART', 'COSMiK']:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 35\u001B[0m \u001B[1;31m# continue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mpred\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mmiss\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mfun\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mmodel\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTrain\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mscaledTest\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mnormalization\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[0mpredicted\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m+=\u001B[0m \u001B[0mlist\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mpred\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 38\u001B[0m \u001B[0mrules\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mname\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mappend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mn\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\AppData\\Local\\Temp/ipykernel_12848/2214823521.py\u001B[0m in \u001B[0;36mcreepy\u001B[1;34m(model, train, test, normalization)\u001B[0m\n\u001B[0;32m 34\u001B[0m CReEPy = Extractor.creepy(model, clustering=Clustering.cream, depth=5, error_threshold=.5, gauss_components=10,\n\u001B[0;32m 35\u001B[0m output=Target.REGRESSION, normalization=normalization)\n\u001B[1;32m---> 36\u001B[1;33m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mextract\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtrain\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 37\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mbrute_predict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mn_rules\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0msum\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0mp\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mNone\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mp\u001B[0m \u001B[1;32min\u001B[0m \u001B[0mCReEPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtest\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Desktop\\psyke\\psyke-python\\psyke\\__init__.py\u001B[0m in \u001B[0;36mextract\u001B[1;34m(self, dataframe, mapping, sort)\u001B[0m\n\u001B[0;32m 379\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mHyperCube\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mcreate_surrounding_cube\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0moutput\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_output\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 380\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_surrounding\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 381\u001B[1;33m \u001B[0mnew_y\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredictor\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpredict\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mdataframe\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0miloc\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m:\u001B[0m\u001B[1;33m-\u001B[0m\u001B[1;36m1\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 382\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mmapping\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 383\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mhasattr\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mnew_y\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;34m'shape'\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_regression.py\u001B[0m in \u001B[0;36mpredict\u001B[1;34m(self, X)\u001B[0m\n\u001B[0;32m 237\u001B[0m \u001B[0mneigh_dist\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 238\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 239\u001B[1;33m \u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mneigh_ind\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mkneighbors\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mX\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 240\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 241\u001B[0m \u001B[0mweights\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0m_get_weights\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mneigh_dist\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mweights\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36mkneighbors\u001B[1;34m(self, X, n_neighbors, return_distance)\u001B[0m\n\u001B[0;32m 877\u001B[0m \u001B[1;33m%\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_fit_method\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 878\u001B[0m )\n\u001B[1;32m--> 879\u001B[1;33m chunked_results = Parallel(n_jobs, prefer=\"threads\")(\n\u001B[0m\u001B[0;32m 880\u001B[0m delayed(_tree_query_parallel_helper)(\n\u001B[0;32m 881\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_tree\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mX\u001B[0m\u001B[1;33m[\u001B[0m\u001B[0ms\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_neighbors\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mreturn_distance\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 61\u001B[0m \u001B[1;32mfor\u001B[0m \u001B[0mdelayed_func\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkwargs\u001B[0m \u001B[1;32min\u001B[0m \u001B[0miterable\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 62\u001B[0m )\n\u001B[1;32m---> 63\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m__call__\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterable_with_config\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 64\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 65\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, iterable)\u001B[0m\n\u001B[0;32m 1083\u001B[0m \u001B[1;31m# remaining jobs.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1084\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m-> 1085\u001B[1;33m \u001B[1;32mif\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mdispatch_one_batch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0miterator\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 1086\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_iterating\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_original_iterator\u001B[0m \u001B[1;32mis\u001B[0m \u001B[1;32mnot\u001B[0m \u001B[1;32mNone\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 1087\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36mdispatch_one_batch\u001B[1;34m(self, iterator)\u001B[0m\n\u001B[0;32m 899\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mFalse\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 900\u001B[0m \u001B[1;32melse\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 901\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_dispatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mtasks\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 902\u001B[0m \u001B[1;32mreturn\u001B[0m \u001B[1;32mTrue\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 903\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m_dispatch\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 817\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_lock\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 818\u001B[0m \u001B[0mjob_idx\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 819\u001B[1;33m \u001B[0mjob\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mbatch\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mcb\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 820\u001B[0m \u001B[1;31m# A job can complete so quickly than its callback is\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 821\u001B[0m \u001B[1;31m# called before we get here, causing self._jobs to\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36mapply_async\u001B[1;34m(self, func, callback)\u001B[0m\n\u001B[0;32m 206\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_async\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mfunc\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;32mNone\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 207\u001B[0m \u001B[1;34m\"\"\"Schedule a func to be run\"\"\"\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 208\u001B[1;33m \u001B[0mresult\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mImmediateResult\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 209\u001B[0m \u001B[1;32mif\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 210\u001B[0m \u001B[0mcallback\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\_parallel_backends.py\u001B[0m in \u001B[0;36m__init__\u001B[1;34m(self, batch)\u001B[0m\n\u001B[0;32m 595\u001B[0m \u001B[1;31m# Don't delay the application, to avoid keeping the input\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 596\u001B[0m \u001B[1;31m# arguments in memory\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 597\u001B[1;33m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mresults\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mbatch\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 598\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 599\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mget\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\joblib\\parallel.py\u001B[0m in \u001B[0;36m\u001B[1;34m(.0)\u001B[0m\n\u001B[0;32m 286\u001B[0m \u001B[1;31m# change the default number of processes to -1\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 287\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mparallel_backend\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_backend\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mn_jobs\u001B[0m\u001B[1;33m=\u001B[0m\u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0m_n_jobs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 288\u001B[1;33m return [func(*args, **kwargs)\n\u001B[0m\u001B[0;32m 289\u001B[0m for func, args, kwargs in self.items]\n\u001B[0;32m 290\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\utils\\parallel.py\u001B[0m in \u001B[0;36m__call__\u001B[1;34m(self, *args, **kwargs)\u001B[0m\n\u001B[0;32m 121\u001B[0m \u001B[0mconfig\u001B[0m \u001B[1;33m=\u001B[0m \u001B[1;33m{\u001B[0m\u001B[1;33m}\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 122\u001B[0m \u001B[1;32mwith\u001B[0m \u001B[0mconfig_context\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m**\u001B[0m\u001B[0mconfig\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m--> 123\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mfunction\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[1;32m~\\Anaconda3\\lib\\site-packages\\sklearn\\neighbors\\_base.py\u001B[0m in \u001B[0;36m_tree_query_parallel_helper\u001B[1;34m(tree, *args, **kwargs)\u001B[0m\n\u001B[0;32m 683\u001B[0m \u001B[0munder\u001B[0m \u001B[0mPyPy\u001B[0m\u001B[1;33m.\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 684\u001B[0m \"\"\"\n\u001B[1;32m--> 685\u001B[1;33m \u001B[1;32mreturn\u001B[0m \u001B[0mtree\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mquery\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m*\u001B[0m\u001B[0margs\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m 686\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m 687\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n", - "\u001B[1;31mKeyboardInterrupt\u001B[0m: " + "ExACT\n", + "CREAM\n", + "2492\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "ExACT\n", + "CREAM\n", + "2493\n", + "GridEx\n", + "GridREx\n", + "CART\n", + "COSMiK\n", + "ExACT\n", + "CREAM\n" ] } ], "source": [ "bartels = pd.read_csv(\"data/bartels.csv\", parse_dates = [1, 2])\n", "\n", - "extractors = ['GridEx', 'GridREx', 'CART', 'COSMiK', 'CReEPy']\n", + "extractors = ['GridEx', 'GridREx', 'CART', 'COSMiK', 'ExACT', 'CREAM']\n", "\n", "TESTB = [i for i in range(2491, 2509)]\n", "\n", @@ -178,12 +232,12 @@ "\n", "missed = {name: [] for name in ['BR'] + extractors}\n", "\n", - "for testB in TESTB:\n", + "for testB in TESTB[:3]:\n", " rules['BR'].append(testB)\n", " missed['BR'].append(testB)\n", " print(testB)\n", "\n", - " data = pd.read_csv(f'data/halffuzzycoefs2B.csv', parse_dates=[0], index_col=0)\n", + " data = pd.read_csv(f'data/pruned.csv', parse_dates=[0], index_col=0)\n", " train, test = getTrainTest(data, testB)\n", "\n", " predicted['index'] += list(test.index.values)\n", @@ -197,16 +251,14 @@ " #dump(model, f\"models/RF/{k}_{name}_{testB}.joblib\")\n", " predicted['model'] += list(model.predict(scaledTest) * s + m)\n", "\n", - " for name, fun in zip(extractors, [gridex, cart, cart, cosmik, creepy]):\n", + " for name, fun in zip(extractors, [cart, cart, cart, cosmik, exact, cream]):\n", " print(name)\n", " #if name in ['GridREx', 'CART', 'COSMiK']:\n", " # continue\n", " pred, n, miss = fun(model, scaledTrain, scaledTest, normalization)\n", " predicted[name] += list(pred)\n", " rules[name].append(n)\n", - " missed[name].append(miss)\n", - "\n", - " break" + " missed[name].append(miss)" ], "metadata": { "collapsed": false, @@ -217,55 +269,26 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 15, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "75.32443332103857 45.80166313557053\n", - "65.44680302846326 41.183118869594864\n", - "65.44680302846326 41.183118869594864\n", - "82.47813971748161 33.34135118914507\n", - "80.09439063082522 39.23197875360054\n" + "90.20644923758856 47.26039351140736\n", + "90.20644923758856 47.26039351140736\n", + "90.20644923758856 47.26039351140736\n", + "73.38650661174208 31.857409982273143\n", + "71.27591988138312 36.1748132343631\n", + "70.26005494494277 33.783890819165826\n" ] }, { "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADP6UlEQVR4nOydd3gU5dbAf29C7713aSKINAERkCsWrlf97Fx7R4OiXntHQYqooPSOoCiKBaVI7713QugtAUINCSFlz/fH7C67yW6yLQ3O73nmSaadOTsz78yZ8573HCMiKIqiKIqiKLmPsJxWQFEURVEURfGMGmqKoiiKoii5FDXUFEVRFEVRcilqqCmKoiiKouRS1FBTFEVRFEXJpaihpiiKoiiKkkvJdkPNGFPJGDPGGLPWZVkhY8wQY8z7xphxxpj62a2XoiiKoihKbiMnPGo3A9MA47LsdeCQiPQFBgJjc0AvRVEURVGUXEW2G2oiMhWIS7P4LmClff1WoKkxpkR266YoiqIoipKbyC0xahVwN97O25cpiqIoiqJcteTLaQXsnACKu8yXsC9LhzHmReBFgKJFi7Zo2LBh1munKIqiKIoSJOvXr48VkfL+7JNbDLUZQFtgqTGmCbBZRM572lBERgGjAFq2bCnr1q3LPi0VRVEURVECxBhz0N99cmLUZ0fgCaCyMeYjY0xh4FugpjHmI+BN4Lns1ktRFEVRFCW3ke0eNRFZDCz2sKp7duuiKIqiKIqSm8ktgwkURVEURVGUNKihpiiKoiiKkktRQ01RFEVRFCWXooZaLmfevHnccsstOa2GoiiKoig5wBVtqC1btozKlSszZ84c57K9e/fSrl07Pv30UwA+++wzhg8fzvDhw3nsscfc9r/xxhv5+++/08k9fvw499xzD926dWPkyJE8/vjjHD9+nHfffZdbbrmFMWPGMGbMGJ566im3/RzrR48eTY8ePZgyZUqmv6Fz587O/ydMmMCePXs8bnfgwAGefvpp5/xHH32UqWxPHDlyhHvuuYe77rrLuUxEaN26Nd26dePChQs+yxowYADnzp0DUGNTURRFUQIgt+RRyxJuvvlmSpYsye233+5cds0111CvXj26dOkCwB9//MHChQspXbo07du3d263cOFCHn/8cb755hvuvvtuN7kVK1akefPmNGzYkK5du7Jv3z5Wr15Nly5dOHfuHM8//zz9+vXj+++/d9vPsf6FF15gy5Yt9O3bl0ceeYSJEyeSkJDAqVOn6NSpEzfddBN9+vRBRKhSpQoASUlJLFmyBICaNWvy1ltv0bx5cyIjI+nTpw9LliwhKiqKcePG0bp1a3799Vd69+7N9u3bmThxIvXq1ePIkSO89957dO3alfLly1OjRg327t3LhAkTnDpWq1aN5s2bc/ToUWbNmkWXLl2YMmUKDRs2pFOnThQrVowhQ4ZgjOH48ePceuutFCxYkK5du/Lxxx+zYMECHn74Ydq3b8/SpUtp1aoVYWFhREdHM2bMGB599FEGDBhAtWrViIyM5KmnnuLw4cO8/vrrvP7664wbN45vvvmGf/75hwoVKlC4cGFeeOGFkN4XiqIoipJXuKINNV8YMWIEb7/9NmfOnKFTp040btwYgH/++Yf+/fszffp01q9fT7NmzejRowfh4eF8++23ACxevJgLFy5QoUIF7rrrLpYuXcrWrVsZNGgQUVFRHo8XGRnJsGHDmDFjBqNHjwagXr16rFixgoIFCzJx4kSaNGnC9OnTWbFiBRcuXOD777+nQIECdOjQwSmnWrVq2Gw2lixZQkxMDB06dGDBggU8++yzAFSuXBmAXr168dlnn9GgQQMeeeQRDhw4wP/93/8RFxfHq6++SseOHT3q+cUXX3DvvffStm1bYmNjqV27NgDx8fH8/PPPLFu2jISEBG699VZWrlxJrVq1nAbagAEDuPfee2nevDkAHTp0oHLlyjz//PPs2rWLXbt28emnn7Jz504+++wzfv75Z4oVK0a3bt14+umnmTt3Lnv37uW2226jSZMmwV5iRVEURcmzXFWGWlRUFLVq1Uq3fMyYMYgId9xxBzfffDMFCxYkNjaWCRMmcN111/HVV1/x008/MWTIELf9OnbsSNeuXd2WNWnShNdff92rDg0aNCAiIoKLFy/y999/061bN15++WXWrFnD4cOH6dWrV6a/Y/r06Zw9e5a3336bBQsWkJiYiDEGAJvN5vzfgYgAuC0vXrx4umWuVKhQgQcffJCHH36Y33//na+++sopy5M8h8xTp06RnJycTp4xBhHBZrN53L9YsWIYYyhUqBCtW7emVatWTJgwgR9//JFRo0Zlek4URVEU5UrkijbUli1bxtmzZ51GxqpVq3j//feJiopi1qxZtGnThjFjxrB5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As7tmzRpQt++ff0+jmss2qxZs/jf//6HMYZy5cppnFoOsG/fvgyNqcy6XGw2GzNmzACsJMoOL63DUFu9ejVr164NSLeTJ09SvXp1Lly4AMDSpUvZtGmTx21nz57N6tWrAzqOooSSY8eOeVyekJCQYajIu+++y44dOyhdujQbN250Lj906BARERF8++23rFy5MuT6ZsSqVauc///zzz/s3r07W4+vKMFyRRlqN9xwAydPniQlJYWCBQuyd+9ewsIu/8Q6derQpEkTt33KlCnjk0fN0zbr1q2jdevWzvny5ctTpkwZSpUqFXR3meI7ixYtYsOGDR6Xr1+/niFDhvDXX3+lW3/mzBmGDx/Oxo0b+eOPPwDLUKtZsybGGJKTk6lUqRIzZ85k0aJFbp4Eh+GVGbt27eK2227jyJEjgBWv5i3P3sWLF93SzBw6dMinY4SSQA1S5crio48+8vjMK1eunNf7NzY2lqioKPbt28djjz3G1q1bnesOHjxI48aNefvtt9NV+7h06VI6WaHMc/nuu+86B5qdOXOGPXv2hEy2omQHV5ShVq1aNT744AMAKleuzK5duyhRooRz/Z133smNN96Ybr98+fJl+GCYMGECPXr0QESIiYmhXLlyAKxevZp27do5t6tRowa1a9emWbNmbl+TvrBs2TIdjOAjf//9t9v8xYsX2b9/f7rzt2vXLtatW0fVqlXdKgwsXryYJUuW8MMPP9CwYUOGDBlCy5YtSU5Opnr16tSuXZtChQpx8eJFSpYsydChQ+nYsaMzkHr//v289957PukaFRVFp06dnIZaWFiYVyPeGON8oaSmpvLMM89w5swZ305KiPjzzz+z9XhK7iQ2NtZjVY7y5ctz8uRJfvvtN1JSUtzuz82bN9O+fXtOnTpFiRIluHjxIgMGDEBEOH36NKVLl6ZEiRJuXf82m41HHnkEESEhIQGbzQaQrusUrLbkL+fOnePGG290etUKFy7M8ePH/ZajKDlJUIaaMaa0MaaGfeoZIp2C0YeaNWs6/9+xYwfXXpt5tpDatWt7HJm3c+dOvvzySxo0aMA999xDVFQU8+fP59Zbb3U7poOqVavStm1b6tat6/dX2y+//OK3cXe1Mnr0aETE2T2TL18+UlNTeeONN9J9nZ88eZIyZcqQL9/lammRkZHs2LGDfPny0alTJ+666y5at25NWFgYXbt2pWbNmlSrVo0jR44476nGjRuzbds2RIRffvnFp/sKrJx7tWvXdhpq4eHhzpeRJxz306FDh+jRowdTpkxJt01iYmLAQd2ZsXfv3iyRq+QdkpKSaN68OVu3buWnn35ydlWKCOXLl+f48eMMHDiQ+fPnuw2U2bNnD/feey+7dl0e+L9gwQKnYWSMwRjj9kF16NAhbrrpJubOncu3337Ltm3bAFixYgUi4pwfP348f/75J9u3b3dLSp0ZkZGRdO3alS1btjh1cDB69Gh/T42i5AgBG2rGmLHAUmAC8D3wRIh0ChrHgyAuLo4GDRpkur2j9mdaFixYwFtvvUXbtm1p164dK1as4Ny5c5QqVYp8+fJRsmRJj/LCwsKcL+PExESfupMaNmzoFkuhWHgKPr548SKxsbG89dZbzmU2m418+fKxYMECt21TUlK48cYbKViwIImJic5tU1JSnN3iDz74IE2bNqVr167OZTVq1HDreixSpAgXL15k4sSJ3HfffYSHh3vVOW05s0KFCjmP7WDixInMnDkz3b7FihUjPj6eyMhImjZt6jE34Pbt21m8eLHX4ztYt25dptukZf/+dOkRlauMPXv20KlTJ2bNmkX58uWdYQXnzp2jVq1abNmyhaZNm7JgwQK6du3KgAEDSExMJCUlhUaNGlG5cmXAev62bNkyQ0/Ytm3bePTRR9m6dSuJiYlERUWRnJzM6dOniYmJYezYscTExJAvXz7eeustRo8ezahRozzK8pTbcvfu3TRo0MDNQHO8HyZMmKBVZJQ8QTAetZIi0lhE/iUinYDnQqVUsCQmJlKkSBGefPJJChcunOn2Du+JK47G7HhxFylShHPnztGoUSPAGojQqVMnrzId+3///fcsW7YsUx1cY+mUy3z11Vdu86dPn+aGG25g9erVnDt3zmnInDhxgkceeYR9+/YBllcgf/78vPXWW9SqVYsaNWq4deUcP37czYgPCwujTJkyzvlq1aqlG4SQmJjIxYsXqV+/PmXKlPHYhZmQkMCAAQMAiI+Pd6ZtsdlsznvC0c3jMAQPHjzoTBFTpUoVoqOjOXToEDVq1KBy5crpAru3bdtG3bp1Mz1348aN86mbZ926dc6PhOTkZO2Cv0LxVrc2bSjBjh07aNSoEWFhYdx6662EhYWRmprKqVOnKF++PJGRkTz//PMUL16cpk2b8tBDDzFz5kzCw8MpWLAgPXv2BKyPz+eee469e/d6TUZ+5MgRqlatyn/+8x/+7//+j9jYWI4dO0ajRo3YuHEjSUlJzJw5kwcffBBjDIMGDaJSpUoeZU2ePNn5/5w5czh79qwzabXjNzhISEigZcuWrF+/3p9TqOQBLl68SLdu3XJajZASjHWw3RhTzGW+dLDKhIpixYpRpUoV7r33Xp+2DwsLS/dy2rhxI82bN3db9tprr3HLLbcA0KpVK6pWrepVZtWqVTl48CA2my3THGunTp2iTJkyHvW42knrZdy5cyddunRh9uzZPPzww8yePZtSpUrx+OOP06JFC2ec17x582jfvj3FihXDGEONGjU4ePAgZ8+eBaBLly60aNHC63ELFy6c7voWLVqU++67D/DuhT127JjzGKtWrXLGRObPn5/Fixdz3XXXObd1dAPNnj3b2Z1TpUoVjh07hs1mIywsjNtuu425c+cCMGbMGI4dO0ZCQgINGjRI93GRlooVK7J8+fIMtwGr0samTZu4dOkSpUqVSuf9U64MevfunW7ZyZMnGTlypNuy48ePU758eb777juMMbRq1Yr169cTGxtLuXLlOHbsGE2aNOGjjz4CoFatWvz666/861//Aqx7HeA///kPtWrVYuvWrdSvX9/tGOvWrWPgwIEcPnwYYwwNGjSgWbNmgPXhctNNN7FkyRLq1KlDYmJiug9uEXG7Ty9duuTW5Tp37lxWrFjhNM6uvfZadu7cCUCJEiXYtGkT999/v4abXGH06tWLBQsW0KpVK48DzPIqwRhqzwAnjDH7jTH7gRyv++mgbt261KpVy699RMQtv86aNWs8Djzwlfbt29O3b19uvvnmTLeNioqiQYMGTm/KpUuXeP3111m6dGnAx8/LHDp0yO0h7Bp8vHv3bm666SZWrVrFQw89xN69e7n77rupW7cuYWFhzi7qyMhIt5dD9erVWbJkCX369KFp06bcdNNNmdbxTBuH9sILLzhTvdSrV8/jMH+HN2D48OEcO3bM6bW79957+fXXX2nfvr1z2zJlynDmzBmn9w8uG2oOg7148eJcuHCBDRs2UK1aNX7++WdiY2O5+eabnV09K1as8Kh/pUqVfM4GLyKcPHmSOnXqcOHChYC6TbOb2bNn57QK2ULarnR/mTt3LsnJyR5Tv/zzzz9u96Qnrr32WiIjIzl16hRl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pQrx8fEcOXKEm2++mb179zpTqzjSyjhGnDk8AImJic5uya1bt3L99dc747Tat2/Pb7/9xo033sjmzZt58cUXadmyZbpjlyhRwtkV6Mi/FxMTQ5UqVVi3bh0333wzBw4ccHaZO9qNa1deWFgYd911V6bpODxx00030bhxY7/3y26ee+45v7vny5UrR61atXjkkUfo3LkzgFtC6+joaCpVquQ0Ijyl9UlISHCO6q5bty533XWX1wTDine2b9/OuHHjuPfeezl+/DiLFy/mqaeeIjY2FhEhMjKSxMREdu3axf79+2nWrFm6soEOwsPDSU1NxRjjzFHqaBc2m401a9bQvn37DEdg79mzhzp16jhjTtetW5ft9ZchCI+aiFwAPnDMG2PaAMHUQDoBuLaAEvZlaY87ChgF0LJlS8koaWluIyUlhfHjx1OpUiXatWtHpUqVMMZ4Tby6fv36dOtWr15NeHi423JHfqPU1FSGDx/Oli1bvJZZSU1NZd++fRkme02LL7FhIsKlS5ecgcibN2+madOmbNu2DZvNRsmSJWnfvj0iwueff86QIUOoWLEi9957L7///judOnVyJpN18Mgjj/ilZ07QoEEDxo8fT9euXZ0v7OykWbNm3Hbbbdx3333ky5ePhg0b0rZtW6exn5YmTZrw73//O8PKBvny5aNdu3bExMRw+vRp5zXYu3cvzZo1o2TJkjn60o6Pj6dhw4a0aNGCtWvXsn79egoVKuT1Xjlx4gRhYWHpYo/8ZenSpdxzzz0sXbqUAgUK8H//939s3bqVmjVr0qJFCxYsWMA///xDt27dnCPPNm3axIIFCyhUqBClSpUiKSmJBx98kHXr1hEfH88tt9zCddddR61atZg6dapPsYlgJVMODw+nQYMGzJkzhxtvvJHffvuNl156yWkgOkrXuRp+ub095RYcsWbNmzdn7dq13HLLLURGRlK9enUqV67svPcc53Px4sWULl2amjVr8sILL7B48WIqVqzotR0qntm0aRNjxoyhYMGCHDlyhJSUFJ555hkeeOAB/v77b1atWkVUVBRPPfUU+/bt47PPPmPHjh1e7+u//vqLdu3akZCQQNmyZWnUqBE2m41Nmzbx3nvvERMTQ3h4OBUrVvSYwH7NmjU0bNiQokWL0qhRI5o0aUKBAgWcMrxlHwg1fnvUjDHd7X/HuU7AkCB1WQnU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PXva9wUFBREeHk6bNm246667rlp6sWLFimRNeN+lSxcmTZrEnXfeSZMmTbySq69pi1oh2Lx5s0ProbmifPnyOZqPHZGamsrYsWPp3r07bdu2ZdSoUcV+nBW4dlduq1atWL9+PU8//TS1a9f2ynJKV/rnP//p9deIiYmxD/T9888/mTx5Mn/88YdLEza/9NJLrFq1iunTp9O1a1eHnyciVKhQgaCgIBo3bpzvsVFRUVy4cIHjx487tEB8VFQUtWvXtrd2uGPRokX079+fGjVq8J///MfetbV06VJKlCjBc889R5cuXXjrrbdo1KgRgwcPZsaMGfbF7Hv27Mlzzz3HK6+8wvHjx3n33XeJioqiT58+DB069KrXa968+VWzxLvj3LlzV21bsWIFr776KgMGDKB3795069aNcePGcf78eVatWkXlypXtPQTh4eEebU0+ceIEvXr14sUXX6RXr16sXr06x/6ff/7ZoYXAr7R792772MbsLl68SP/+/Rk7diyHDh0iPDzcvi8qKsrloSUHDhygTp06VKpUiZ07d5KUlESZMmWoU6cOw4cPdylmYbtw4YJ9iShvMMawYsUK7r333jyPKWh97BIlStCnTx+2bNnCgQMHmDNnjqfT9Dkt1Lxs06ZN1KpVy2vxAwIC2LFjB9OmTeOLL75g/PjxTJgwIddjFy5cyMSJE5kxYwbPPPMMkZGRNG7cmBdeeKHITwrqCa5OSDx48GCqVavGXXfdlWP805YtW3Idv3Py5EmGDx/O1KlTnX6tkydPEhMT49UbSObOncu5c+f4/PPPycjIYObMmTRo0ID169dTooTzjfAiwosvvshrr73m1M83MjKSWrVqMXbsWIdeN/vAe0fcf//9/PDDD5w9e5bXX3+do0ePOpzbiRMn+PHHH/noo48oVaoUISEh3H777bRv355p06Yxfvx4goKCcm05CQwMpEuXLsycOZOHH34YsLp0AwICuPfeexkwYAAdO3YEyLX1sWbNmvY1Fd2VkZGR65x3hw4dytF6GhYWRv/+/Xn77be5ePEijz32mH1f5cqVPT5OVkQICwvLdYmy5ORkoqKiSExMZN26dbz33nv25cYuXLhg71rMPkmwMQZjDJGRkbl2NYeFhTFw4EAuXLiQ4/Pj6l3dp06d4sSJE4SEhNCxY0cOHTrEI488AkC7du2IiIgoEncvLliw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\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "
", - "image/png": "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q65QoULO7Zs1a0bfvn39Po5rLNq0adP43//+hzGGChUqaJxaHrBr165sjamculxsNht//fUXYCVRdnhpHYba8uXLWbFiRUC6HT16lJo1a3L27FkAFi5cyNq1az1uO336dJYvXx7QcRQllBw6dMjj8uTk5GxDRd588002b95M2bJlWbNmjXP5vn37iImJYdCgQSxdujTk+mbHsmXLnP///fffbN++PVePryjBclEZai1atODo0aOkp6dTuHBhdu7cSVTUhZ9Yr149mjVr5rZPuXLlfPKoedpm5cqVtGvXzjlfsWJFypUrR5kyZYLuLlN8Z968eaxevdrj8lWrVjFkyBB+//33LOtPnjzJsGHDWLNmDb/++itgGWq1a9fGGENaWhpVqlRh6tSpzJs3z82T4DC8cmLr1q3ccMMNHDhwALDi1bzl2Tt37pxbmpl9+/b5dIxQEqhBqlxcvPvuux6feRUqVPB6/x47doy4uDh27drFgw8+yIYNG5zr9u7dS9OmTXn99dezVPs4f/58FlmhzHP55ptvOgeanTx5kh07doRMtqLkBheVoVajRg3efvttAKpWrcrWrVspVaqUc/3NN9/MVVddlWW/AgUKZPtgGD9+PD169EBESEhIoEKFCgAsX76cTp06OberVasWdevWpWXLlm5fk76waNEiHYzgI3/88Yfb/Llz59i9e3eW87d161ZWrlxJ9erV3SoMzJ8/nwULFvDNN9/QuHFjhgwZQps2bUhLS6NmzZrUrVuXIkWKcO7cOUqXLk1sbCxdunRxBlLv3r2bt956yydd4+Li6Nq1q9NQi4qK8mrEG2OcL5SMjAwef/xxTp486dtJCRG//fZbrh5PiUyOHTvmsSpHxYoVOXr0KD///DPp6elu9+e6devo3Lkzx48fp1SpUpw7d44BAwYgIpw4cYKyZctSqlQpt65/m83G/fffj4iQnJyMzWYDyNJ1ClZb8pfTp09z1VVXOb1qRYsW5fDhw37LUZS8JChDzRhT1hhTyz71CpFOwehD7dq1nf9v3ryZyy/POVtI3bp1PY7M27JlC59++imNGjXi9ttvJy4ujtmzZ3Pddde5HdNB9erV6dChA/Xr1/f7q+2HH37w27i7VBk1ahQi4uyeKVCgABkZGbzyyitZvs6PHj1KuXLlKFDgQrW0bdu2sXnzZgoUKEDXrl259dZbadeu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i3Pdr79S86+/SH/qKUY1b86hQ4d45513+P777zl9+jTly5fnoYce8uu3XIzs3LmTn3/+mVKlSpGRkcHZs2eJiYnx6dr4yvPPw9ChF+ZjYiA21lp++dDneZYRDKc7PUwszz0HV1wxlJiYmJAdX1EUJa/Yv38/EydO5IUXXqBUqVKA5bAZMWIEp0+fJioqitdff51vvvmG66+/nipVquSxxkHi+sCPjob09DxVxxizSkTa+LWTrxYdcCOWJ+0z4DBWotvHgaL+Woehmvz1qDnI7Lk6fvy4fPHFF8753377Tb766iuP8ThTpkyx4jqy8bc7A1iNEdtzz0nKU085vXAZUVEhicW6mElKSnJ2gTqKS4e6S9TX7pLoaP8LaSuKokQyycnJMmjQIFm+fLmIWBUedu3aJWfOnJGhQ4fKqVOnInOwQKDkVXZbDxCARy3KD5vuW2AZUAi4XkQ6isg4ETnnl2UYAdSqVcttvly5cjRq1Ii4uDgSEhI4d+4cL774IpUrV2bRokXs2LGDl156ibS0NPbs2UPdunUtF4yI5Y5xQbAKnxoAEcywYRQePdrpfYt69llKly6dC78y/1KsWDEKFiwIQJEiRYiJiaF///4kJiY6tzl9+jS///57wMeIjc1y6bJgDNx88x7at28f8HEURVEijaJFi9KjRw82bNjAZ599RvXq1albty4lS5bklltuYeTIkTzyyCN5rWboiI21PGmxsXmtSUD43PVpjJkAPCMiKeFVyXcC6fr0RlJSEj/++CMpKSk89thjFClSBIC///6bc+fO0aZNGz799FO6detGp06dvAvK3K/miqOPTfGbc+fOMWTIEFq2bEmXLl3o27cvHTt25MyZM9x1110By23WDDZutP43Bp57zrpEGzZsIDk5me3bt/Pwww+H6FcoiqJEFjabjagof3w2SjAE0vXpj6FWXESSAtIsTITSUAMYMGAAJUuW5Nlnn/W4fvXq1bRq1cp3ga5WgBppIWHt2rWMHz+enj17UrlyZZYtW8batWs5ffo099xzD5dddlnQx5g5cyYnT56kVKlS3HzzzSHQWlEURVHCbKhFIqE21EaNGsV9992nXZP5EBGhb9++vPXWWxw/fpyDBw/SokULv+WcOnWK7777jueeey70SiqKoiiXNGqoKZc0Bw8eZMiQIdSvX59z585RtGhRihUrRpEiRbj1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V67s9OBt5TnOrnFXnGVkZDg0niQyMpL27duTlpbGV199lWvfiRMnqFy5MlWqVHHoNY8fP86uXbs4fPgw119/PX/72994+OGHadCgASdOnKBbt27UqFHDfkdafi3XgYGBtGnThrFjx+ZbQPqjAwcOcP311wNwyy230KlTJ+bPn+/jrCw//vgjEyZMsF80rwZxcXG53puRkZEu331sjOG9997LVVjPnj27xE1S3qNHjzznD728UJs9ezaDBw9m8ODBTJw4kTlz5vDf//6XzMxMvv76a0aNGsULL7zAwIEDGT9+PIsXL2br1q1+cz54kxZqRcAYQ0ZGhlcWrr7mmms4ceKES8/dtGlTrgkJlfOy5wM6cOCAw2OziqsjR444PL1Mo0aNuOuuuwgJCWHdunX27fPnz+fBBx90+DVr167Njh077K0cYWFh9sJl0qRJlC1blgcffJCZM2eSnp6eq7C5XNu2benTpw8hISGMHz++yO7o++OPP4iPjyc1NZVJkybl6ro5evQo58+fz/e5v/zyS65WjBo1ahAXF+fNdO1yTsdwuaNHj3Ls2DEef/xxn1woPfkzOHr0qH2ur8vXrrz++utd7jI/deoUt956K1u2bAGsaT82bNjA6tWr3U+6GMnvxqCyZcvae4MWLVpE3bp1KV26NEFBQQwdOpR77rkHgH79+tG8eXOGDBlCxYoVCQsLo3fv3kRERNCtWzeioqKu+rFuRX7XZ0m0fv16brrpJq/Erly5skszuKelpfHTTz8hIjRq1MgLmZUMUVFRHDhwgDVr1pCens5bb73l65S8pqAiKD+dOnVi8uTJVK5cmaioKIKCghweqykihISE8Msvv9C3b98r9mcvxxYQEEDv3r2ZN28eoaGhtG3bNt+YpUuXpk2bNlx33XV8+OGH9OvXz6nvJy9xcXF8+umnXHvttTz22GO59i1fvpxjx44RGhrK/v376dSpE8OHD+epp56iatWqzJ07l7CwMDIyMjDG0L59exo0aMCECRPIysri2LFjV8SsVKkS586dIzIy0u3c85Oens6ECROoWrUqISEhlC5dmh49elCunDUv+eLFi3n22WcJCgoiIcGzU14uWrSIo0ePkp6eTkJCAi+88EKuuwYPHDjAhAkTGDt2rFNxV61axQ8//MDLL79sn24ie/uhQ4do2rQpf/75JzfeeKN9X5UqVVye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j7Jw4UJ69erFwoULr5h8siBr1qyhf//+ALRu3ZrSpUuzbt06Nm7cyKBBg3j66aftBWvOOZTAKnIGDBhQ4MSjzmjfvn2uxyJS4ADz7Avj5fNlAWzfvp3p06dTrlw5MjIyCpzR/69//SvffvstqampHD58mC5dutgvgD///DOxsbG0a9eOV199Nc88VcmSc94qT8p5kapevTq7du2yDynYs2cPERERnDx5Mte5XdCHyex9tWrV4osvvuCOO+5wOJcaNWqwcuVKHnnkkVzbnZlmpmfPnnzyySccOXKE9957L88uwTFjxtiHmDg6uXlkZCTnzp3DGIOI8Msvv7B161YiIyPdvlFo//79JCUlsWbNGq677jp7d3F+EhMTCQsLIyYmxiOFuj+pV6/eFd3pXbt25e233yYoKIghQ4bkuomudOnSDBo0iIkTJxIREUGHDh349ddfadiwIXFxcQwbNoxrr72WypUrs3r1atLS0ti7dy9Dhw7lzTffJCYmhoSEBP7v//6P+vXrM2HCBF588UVOnjzp8vCiYl2ogXVxuueee5g1axYLFy4kJSWFFi1akJKSwooVKyhdujTt27enYsWKeY4byG88RP/+/XPNdlyqVCm6du3KDTfcYJ8l/YEHHmDBggU8+uij7Nq1y6V10PxR5cqVOXbsGA899BCrV6+mZcuWXh27tHr1au69916+++479u3bR7ly5ejWrRtTp07Nc6LTnLZv386KFStyfSIXEZo0aWK/ODjSohgREeH2jQTe0KRJE37++WeCg4OvuNhc7tprr2Xu3LmICEOGDOGjjz5i+fLlANx4441ev/tYKbDm4Mue965KlSq5WsdXr15Nz5497eMpJ0+eXOgceaGhoYBVqG3evNmpHot7772Xjh07OvcNXCYsLIwBAwawZ88e5s2bR3JyMqmpqQQGBhITE0P16tVp0qQJd999N+PGjXP4jluwWpTGjh1Lhw4d2LlzJ3379mXmzJlXLGVVkD/++IMpU6bw7rvvAlahPG/ePAYPHkxKSgrTp08vtCjfvn17ob+Hq0n2ON86derkOdNBUFAQL730EmD9PN9//30aNmzIrFmzmDhxIlu3bqVly5b247OL7caNGxMQEEBWVhY//PADCxcupHnz5sydO5cDBw7Y38vOKvaFGlg/9EceeQRjDGlpaU6dKPkpW7bsFdMmZP8ismUP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\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAmoAAAHsCAYAAABi04EnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADNg0lEQVR4nOzdd3hUVfrA8e9JQu+9V2kiSJcmImtlXfVnWWUta0UUFXWt7FpQUUQsKL0j2FAsqIj03nsnhN4SIARCIKTO+/vjzgwzySSZmkzg/TzPPMncuffcMzP33nnvqUZEUEoppZRS4SeioDOglFJKKaU800BNKaWUUipMaaCmlFJKKRWmNFBTSimllApTGqgppZRSSoUpDdSUUkoppcJUvgdqxpjqxpjxxpi1LsuKG2OGG2P6G2MmGmOa5He+lFJKKaXCTUGUqF0LzACMy7IXgUMiMgj4HJhQAPlSSimllAor+R6oich0ICnL4tuAlfbXtwKtjDFl8ztvSimllFLhJFzaqFXFPXg7a1+mlFJKKXXZiiroDNidAMq4PC9rX5aNMeYp4CmAUqVKtWvWrFnoc6eUUkopFaD169fHi0gVX7YJl0BtJtAZWGqMaQlsFpGznlYUkbHAWID27dvLunXr8i+XSimllFJ+MsYc9HWbguj12R14GKhhjHnTGFMC+AKoZ4x5E3gZeCK/86WUUkopFW7yvURNRBYDiz289Gx+50UppZRSKpyFS2cCpZRSSimVhQZqSimllFJhSgM1pZRSSqkwpYFamJs3bx7XX399QWdDKaWUUgXgkg7Uli1bRo0aNZgzZ45z2d69e+natSvvvPMOAO+++y6jRo1i1KhRPPjgg27bX3PNNfz+++/Z0j1+/Dh33HEHffr0YcyYMTz00EMcP36c119/neuvv57x48czfvx4HnnkEbftHK+PGzeOfv36MW3atDzfw4033uj8f/LkyezZs8fjegcOHODRRx91Pn/zzTfzTNuTI0eOcMcdd3Dbbbc5l4kIHTt2pE+fPpw7d87rtIYMGUJiYiKABptKKaWUH8JlHLWQuPbaaylXrhw333yzc9kVV1xB48aN6dmzJwC//PILCxcupEKFCnTr1s253sKFC3nooYf47LPPuP32293SrVatGm3btqVZs2b06tWLffv2sXr1anr27EliYiJPPvkkH330EV999ZXbdo7X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12AqryoXm83GzJkzAWsQZUcprSNQW716NWvXrvUrbydPnqROnTqcO3cOgKVLl7Jp0yaP686ePZvVq1f7tR+lgunYsWMelycnJ+faVOT1119nx44dVKhQgY0bNzqXHzp0iL59+/LFF1+wcuXKoOc3N6tWrXL+/9dff7F79+583b9SgbqkArXWrVtz8uRJMjIyKFasGHv37iUi4uJbbNiwIS1btnTbpmLFil6VqHlaZ926dXTs2NH5vEqVKlSsWJHy5csHXF2mvLdo0SI2bNjgcfn69esZPnw4v/32W7bXT58+zahRo9i4cSO//PILYAVq9erVwxhDeno61atX588//2TRokVuJQmOwCsvu3bt4qabbuLIkSOA1V4tp3H2Lly44DbMzKFDh7zaRzD5G5CqS8ubb77p8ZpXuXLlHI/f+Ph4YmJi2LdvHw8++CBbt251vnbw4EFatGjBq6++mm22j9TU1GxpBXOcy9dff93Z0ez06dPs2bMnaGkrlR8uqUCtdu3a/Pe//wWgRo0a7Nq1i7Jlyzpfv/XWW7nmmmuybRcVFZXrhWHy5Mn069cPESEuLo7KlSsDsHr1arp27epcr27dujRo0IA2bdq43U16Y9myZdoZwUu///672/MLFy6wf//+bJ/frl27WLduHbVq1XKbYWDx4sUsWbKEr7/+mmbNmjF8+HDat29Peno6derUoUGDBhQvXpwLFy5Qrlw5RowYQffu3Z0Nqffv388bb7zhVV5jYmLo0aOHM1CLiIjIMYg3xjh/UDIzM3nsscc4ffq0dx9KkPz666/5uj8VnuLj4z3OylGlShVOnjzJTz/9REZGhtvxuXnzZrp168apU6coW7YsFy5cYMiQIYgICQkJVKhQgbJly7pV/dtsNu6//35EhOTkZGw2G0C2qlOwziVfJSYmcs011zhL1UqUKMHx48d9TkepghRQoGaMqWCMqWt/DAhSngLJD/Xq1XP+v2PHDq68Mu/RQho0aOCxZ97OnTv5+OOPadq0KXfccQcxMTHMnz+fG264wW2fDrVq1aJz5840atTI57u2H374wefg7nI1btw4RMRZPRMVFUVmZiYvvfRStrvzkydPUrFiRaKiLs6WFh0dzY4dO4iKiqJHjx7cdtttdOzYkYiICHr16kW9evWoXbs2R44ccR5TLVq0YNu2bYgIP/zwg1fHFVhj7jVo0MAZqEVGRjp/jDxxHE+HDh2iX79+TJs2Lds6KSkpfjfqzsvevXtDkq4qPNLS0mjbti1bt27lu+++c1ZVighVqlTh+PHjfP7558yfP9+to8yePXu488472bXrYsf/BQsWOAMjYwzGGLcbqkOHDtGlSxfmzp3LF198wbZt2wBYsWIFIuJ8PmnSJH799Ve2b9/uNih1XqKjo+nVqxdbtmxx5sFh3Lhxvn40ShUIvwM1Y8wEYCkwGfgKeDhIeQqY40KQlJRE06ZN81zfMfdnVgsWLOCVV16hc+fOdO3alRUrVpCYmEj58uWJioqiXLlyHtOLiIhw/hinpKR4VZ3UrFkzt7YUyuKp8fGFCxeIj4/nlVdecS6z2WxERUWxYMECt3UzMjK45pprKFasGCkpKc51MzIynNXi9957L61ataJXr17OZXXr1nWreixZsiQXLlxgypQp3HXXXURGRuaY56zTmRUvXty5b4cpU6bw559/Ztu2dOnSnD9/nujoaFq1auVxbMDt27ezePHiHPfvsG7dujzXyWr//mzDI6rLzJ49e+jRowezZs2iSpUqzmYFiYmJ1K9fny1bttCqVSsWLFhAr169GDJkCCkpKWRkZNC8eXNq1KgBWNff9u3b51oStm3bNh544AG2bt1KSkoKMTExpKenk5CQQFxcHBMmTCAuLo6oqCheeeUVxo0bx9ixYz2m5Wlsy927d9O0aVO3AM3x+zB58mSdRUYVCoGUqJUTkRYi8jcR6QE8EaxMBSolJYWSJUvy73//mxIlSuS5vqP0xJXjZHb8cJcsWZLExESaN28OWB0RevTokWOaju2/+uorli1blmceXNvSqYs++eQTt+cJCQm0bt2a1atXk5iY6AxkTpw4wf3338++ffsAq1SgSJEivPLKK9SvX5+6deu6VeUcP37cLYiPiIigYsWKzue1a9fO1gkhJSWFCxcu0KRJEypWrOixCjM5OZkhQ4YAcP78eeewLTabzXlMOKp5HIHgwYMHnUPE1KxZk9jYWA4dOkTdunWpUaNGtobd27Zto1GjRnl+dhMnTvSqmmfdunXOm4T09HStgr9E5TRvbdamBDt27KB58+ZERERwww03EBERQWZmJqdOnaJKlSpER0fz5JNPUqZMGVq1asU///lP/vzzTyIjIylWrBgDBgwArJvPJ554gr179+Y4GPmRI0eoVasW//jHP/i///s/4uPjOXbsGM2bN2fjxo2kpaXx559/cu+992KMYejQoVSvXt1jWt9++63z/zlz5nDmzBnnoNWO9+CQnJxM+/btWb9+vS8foSoELly4QJ8+fQo6G0EVSHSw3RhT2uV5hUAzEyylS5emZs2a3HnnnV6tHxERke3HaePGjbRt29Zt2QsvvMD1118PQIcOHahVq1aOadaqVYuDBw9is9nyHGPt1KlTVKxY0WM+LndZSxl37txJz549mT17Nvfddx+zZ8+mfPnyPPTQQ7Rr187ZzmvevHl069aN0qVLY4yhbt26HDx4kDNnzgDQs2dP2rVrl+N+S5Qoke37LVWqFHfddReQcynssWPHnPtYtWqVs01kkSJFWLx4MVdddZVzXUc10OzZs53VOTVr1uTYsWPYbDYiIiK46aabmDt3LgDjx4/n2LFjJCcn07Rp02w3F1lVq1aN5cuX57oOWDNtbNq0idTUVMqXL5+t9E9dGgYOHJht2cmTJxkzZozbsuPHj1OlShW+/PJLjDF06NCB9evXEx8fT+XKlTl27BgtW7bkzTffBKB+/fr8+OOP/O1vfwOsYx3gH//4B/Xr12fr1q00adLEbR/r1q3j888/5/DhwxhjaNq0KW3atAGsG5cuXbqwZMkSGjZsSEpKSrYbbhFxO05TU1Pdqlznzp3LihUrnMHZlVdeyc6dOwEoW7YsmzZt4u6779bmJpeY999/nwULFtChQwePHcwKq0ACtceAE8aY/caY/UCBz/vp0KhRI+rXr+/TNiLiNr7OmjVrPHY88Fa3bt0YNGgQ1157bZ7rxsTE0LRpU2dpSmpqKi+++CJLly71e/+F2aFDh9wuwq6Nj3fv3k2XLl1YtWoV//znP9m7dy+33347jRo1IiIiwllFHR0d7fbjUKdOHZYsWcKHH35Iq1at6NKlS57zeGZth9a7d2/nUC+NGzf22M3fURowatQojh075iy1u/POO/nxxx/p1q2bc92KFSty+vRpZ+kfXAzUHAF7mTJlOHfuHBs2bKB27dp8//33xMfHc+211zqrelasWOEx/9WrV/d6NHgR4eTJkzRs2JBz5875VW2a32bPnl3QWcgXWavSfTV37lzS09M9Dv3y119/uR2Tnlx55ZVER0dz6tQpKlWqxNVXX+3W5hOgf//+2YIxh3379rntwxjDihUreOmll3jmmWeyre9otxYTE0Pjxo2z9bA2xrBx40YmTJgAWCUoR48epWrVqoBVStelSxd27tzpvElu27at84e7S5cuTJ06lcaNG+dY0pf1hllnVSgczp07x/jx43niiSf8niIyHAUSqH0rIiVFpIGINMCanSAsXH/99VxxxRU+b9e/f39n6YVjBgN/VaxYkVOnTtGiRQuPY6udPn3aeTHYtm0bjRs3plmzZuzatYuYmBiuu+66Ah8csqDMmDGDzZs3k5qaSosWLdxKrtLS0ihevDi1atWiZMmSPP/8824llldffTUzZ87kjjvucEuzWLFiLFq0iIEDB9K5c2ev8vHCCy/k+Frp0qU9DtFx7Ngx7r//fsqUKcPDDz/srNKuWrUqI0aMAKwflhIlSlC3bl127Njh1taxbNmynDlzxu2YKV26NMuXL+fWW2/lpZde4oknnqBChQqcOnUKEWHKlCk5lsTmVUK7evVqmjVrhjGGuLg4GjZsSFJSEl9++aXH9UeOHJlrevlp6tSpBZ0Fv9hsNq968+7evZtz5845S64cfC11nzZtGkuWLHHeZLhKSkqiWrVqpKSksGbNGt577z3q1q3rtk6pUqVITk4mPj6eSpUq8dRTT2VL5+qrr85x/wMHDnSOTQjWueiouq9du7bbujabjYSEBBo3bszp06fp2LEjN910k9s6VatWZf78+Vy4cIHFixfz1FNPsW/fPurVq0dmZqazN/6OHTuczVNKlSrF+fPnAahXrx4bN26kRo0a2XqEg1Ut+vbbb7st83Z2kQ0bNlxSJTmFQVxcHAcPHkREaNCgAW+99Zaz48qlwu9ATUTeMMaUs8/3WdY+tVShVbp0ae6991527txJWlqaV23b8jJp0iSMMR6H//joo48YNWoUmZmZzrZMDRo0YO/evURHR9O5c+fLdiw2m83Gzp07OXXqFF26dCE6Oprp06ezaNEi5zq9e/f2uK0xho8++shjoP7ee++5DXicF9ehXTzx9IPpaMf40EMP5bhdamoqDRo0oG7duvzxxx9cffXVpKenU6RIEYwxHDx40Nl7GeDhhx/mueeeA6z3V7NmTQCuuuoqYmJiOHLkCPHx8aSlpTFv3jzndo4qX0eP5lOnTjnnMwWrNGXbtm387W9/o23btvz+++9cccUVnD592llN5CojI8OrqtT8cujQIa/Hswsn0dHRfP3118ye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nDlKAFJSUpzVklu3buXqq692ttPq1q0bP/30E9dccw2bN2/mqaeeon379tn2XbZsWWdVoGP8vbi4OGrWrMm6deu49tprOXDggLPK3HHeuFblRUREcNttt+U5HIcnXbp0oUWLFj5vl9+eeOIJn6vnK1euTP369bn//vu58cYbAdwGtI6NjaV69erOIMLTsD7JycnOXt2NGjXitttuy3GAYZWz7du3M3HiRO68806OHz/O4sWLeeSRR4iPj0dEiI6OJiUlhV27drF//37atGmTbdpAh8jISDIzMzHGOMcodZwXNpuNNWvW0K1bt1x7YO/Zs4eGDRs625yuW7cu3+dfhgBK1ETkHPBfx3NjTCcgkDmQTgCuZ0BZ+7Ks+x0LjAVo37695DZoabjJyMhg0qRJVK9ena5du1K9enWMMTkOvLp+/fpsr61evZrIyEi35Y7xjTIzMxk1ahRbtmzJcZqVzMxM9u3bl+tgr1l50zZMREhNTXU2RN68eTOtWrVi27Zt2Gw2ypUrR7du3RAR3nvvPYYPH061atW48847+fnnn+nRo4dzMFmH+++/36d8FoSmTZsyadIkevXq5fzBzk9t2rThpptu4q677iIqKopmzZrRuXNnZ7CfVcuWLfn73/+e68wGUVFRdO3albi4OBISEpzfwd69e2nTpg3lypUr0B/t8+fP06xZM9q1a8fatWtZv349xYsXz/FYOXHiBBEREdnaHvlq6dKl3HHHHSxdupSiRYvyf//3f2zdupV69erRrl07FixYwF9//UWfPn2cPc82bdrEggULKF68OOXLlyctLY17772XdevWcf78ea6//nquuuoq6tevz/Tp071qmwjWYMqRkZE0bdqUOXPmcM011/DTTz/x9NNPOwNEx9R1roFfuJ9P4cLR1qxt27asXbuW66+/nujoaOrUqUONGjWcx57j81y8eDEVKlSgXr169O7dm8WLF1OtWrUcz0Pl2aZNmxg/fjzFihXjyJEjZGRk8Nhjj3HPPffw+++/s2rVKmJiYnjkkUfYt28f7777Ljt27MjxuP7tt9/o2rUrycnJVKpUiebNm2Oz2di0aRNvvPEGcXFxREZGUq1aNY8D2K9Zs4ZmzZpRqlQpmjdvTsuWLSlatKgzjZxGHwg2n0vUjDHP2v9OdH0AwwPMy0qgnjHG0RCiKzAzwDTDSlRUlFv3+AYN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W7ZsyTZ80RtvvEGlSpWcgyWXL1+eY8eOOQs93n77bc6fP8/EiROJj4/nwIEDzg4mtWvXZu/evR5nfPGGP23UutsHo73eGPO2MeZt4EWgjl85UHlq0qQJP//8c7YG3AkJCezatYtatWqRlpbm/CGpUqWKW7H7qVOnSEpKck4zVKNGDVasWMG8efO45557WLduHT/99BMXLlxwKz1wzHdqjOHIkSMcOnSI2NhYqlWr5pzk/P7773c2bAarVM1RJF2sWDFnNVvWEghX3s69qfLHPffck+vrjkntwerM4Fp96bBp0yY6dOhAq1atSE5OdqvC2LlzJ3feeSfR0dG0b9/e2ei9UaNGLF26lGHDhnHmzBkqVKjAFVdcwZYtW+jYsSOzZs3Ktf1dREQE//rXv5y9mrPeva9YsYInnniCPXv25JhGWloac+fOZcWKFYwePZpt27bRsWPHXHvwVapU6bKd7u1S5rjxdHDUVGR93TGAscpZeno611xzDRs2bOC3337jxhtvZM6cOZw6dYqqVasSERHBjh07gjrQfFb/+9//PC53nZKtaNGipKens3r16jxLyBs1asQbb7zhbAa0YMEC9u/fz2uvvca4ceOYNm0aZcuW5fz584gIDzzwgHPmGF/5U6J2BjgAJAIH7Y89QM4zWKuAlC9fngULFmQLaGrXrs369etp1aqV2/gxlStX5siRIxQvXpzixYvz3Xff8euvvzJ16lSefvppatSowddff83jjz/uLK2Li4tzDq7pKLI/ceIEVatWpXz58uzcuZMHHniAYcOG0alTJ+rUqcPixYv5xz/+wZYtW5zjut16663ONjm33nprrpM1q0uD4852x44dzlKEli1b0qxZM6Kionj99deBiz9sjul/0tPTueqqq5yBXt26dZk2bRq33Xab8wexUaNGLF++nM6dOzNlypQ8h5aoX78+V111FR9//DFjxoxxm0R+586dNG/ePFvJ7oQJE5wjlO/fv5+xY8eyevVqbr75ZoYMGcLNN9/M9u3bc6yer1ixoo6XdYly3KxC9htgsI79cJqxI1zt27ePJk2aEBkZyd69e2nZsqXbrCIVKlRgz549zim+QiGnzjpZS9pEhJSUlDxL7Jo1a0bv3r1JTk4mKSmJOnXqsGzZMpo1a0avXr345z//Sd26ddmxYwdly5YNqE2qP23UNovIV8BTIvKV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fUefPn0oUaIEHTp0YMWKFRhjHO56PnLkCC1btiQuLo6LFy8ye/Zsxo8f73Zz/7Zt21i4cCEvvfQSt912G2B9joYOHUqVKlUQEYYOHerWazirVKlSTs1a3q5dO6fHgkZERNCrVy/Kli3LgAEDaN68OWXKlGHOnDmcOXOGmTNn2udpy8jIYNGiRSxevJi+fftSv359n602Ur58eaZPn87ly5eZO3cuZ86cISUlhYceeojRo0df1Qr066+/MmrUKCZPnszXX39t379161YGDBhw1d+LBx54INfXze38yCrUcpPffHq52bNnD/Xr189zf9b5VatWLaKioujYsSMiQpcuXfjiiy84ffo0f//731m8eDEVKlTg999/B2Dt2rX2z3XTpk3Zvn07s2fPZuDAgV4ZlhIcHEytWrXy3O/p7mtvuHDhgtfH0l28eNErXavh4eHUr1+f+fPnF4mWy4JooVaA9PT0XCfgW7p0KUeOHLlqe2pqKuPHj2f06NGUL1+eChUqFEKWOYWHh3Pw4EG++uor1qxZQ0RERIEXlPr163tkvI4vZWZmMnLkSH744Ydc93/55ZdMnjyZb7/9losXLzJz5kw6d+5sn0Rx+/btNGrUyK0cqlWrxp9//klsbCyVKlXiqaeesheCWWtPZhUfgYGBBAYGsnr1aoYNG+bwexQROnbsyIwZM2jXrh19+vQhNDTUpZtKwBrM+8MPP9CrV69iPSmziFCzZk3atGlDpUqV+O6772jfvj3Tp09n4sSJDB8+nEaNGvHKK694rBXAVY888gi7du3iscceo0ePHtx+++20adOGihUr0rt3bz766COmTJnC6tWrSUxMZOXKlQwaNIiXXnqJ1q1b8+mnn9ovYBEREZw+fZpTp06RkZFBfHy8faC2I66cZgGs2egnTpxI9+7dC7xQJiYm8uabb/LJJ58we/Zs6tatm+tx69ev5+LFi5QtWxawCvOsFqno6GgaNmxIx44dCQoK4vjx43Tt2pW9e/cC1mc8a0xdkyZN2LRpExkZGbm2HLorOTm5wBbNChUq+O20SsYY0tLSCuUzfuHCBZo1a+aV2O3ateP6669n5cqVAFy6dInp06fnuAs9uzNnzvD999/nO+Guz2T13RfF/5o3b268ITMz0/7vMWPGmHfeecfEx8fnOGb48OFm+vTpVz133Lhx5uzZs17Jy1lnz541W7ZsyfF+8rN27VozYsQI89tvv3k5s6tlZGS4HWPmzJnm8OHDZvz48Tne86RJk8z27dvNwoULjTHW7+i9994zSUlJxhhj1q9fb7Zs2WImTZrkdg7nzp0zU6ZMMRMnTjQJCQnGGGOWLFliPv/8czNx4kQTGxub4/jff//ddOvWzcyfP9/s2bPHrF+/3hw+fDjX2ElJSebTTz/NdV9GRoYZO3asw3muXr3apKenm4SEBPPhhx+aU6dOOfxcVXRs377djBw50ly8eDHH9mXLlpnPP//crFq1yhhjzOXLl820adPMRx99lOfnLz9TpkzJ8fizzz4zSUlJZu7cuebMmTM59l15rq9cudLs27cv39jz5883r7zyivn999/tOecnLi7OpKenm8mTJ5tTp06Z2bNn59jfpUsX8+effxYYJ+v1nbF+/Xqzbdu2Ao+bNm2aSUxMdCq2Ny1ZssSMHz/ezJw50/Tt29ds3LjR66/53//+16Snp3v1NT788EPz+++/mw8++MAkJCSYFStW2K8FxhiTmppqli5dakaMGGF27NhhPvjgA3PhwgVjjPVZdfR66Og1DIgxTtY6RbpFLT4+3t607ajMzEwuX75sr56TkpJITU1lzZo1zJgxg/Hjx/Paa69x4sQJNm3aRLNmzRg6dCjjxo1j9erVxMfHM3z4cNq2bXvV0jXbtm2jdu3aXr1t3hmRkZE0a9bM4e6ZW2+9lcGDB7NkyRL7nFSmkJqN+/fvz7x58zh//jxjx461fwty1JYtWyhTpgzR0dG0atWKkSNHsnLlSmJjYwkJCWHYsGH2QbFPPPEEL730kn2tultuuYXFixd75L1GRERQq1YtbrrpJvu39QceeIBLly7x8ssvXzVDd506dRg2bBiPPfaY/SaBHTt28PHHHzNx4kRWrVpl/4a3bNmyPLukAgICCA0NtXc15fZejDH89NNPjB49mvT0dD766CMWLlzIww8/7JOWX+V9jRs3ZtCgQVd1L91///0kJydz++23A9b4rB49etCvXz+io6Pdft3k5GRKlSplb2HObsCAATm27d+/P99uQrAG5999993s2rUr11nur3TdddfZW65XrFhx1ZjDkSNH2sfwFkRESEpKKvDvQ1paGsYYdu3alWOKj7x07NjRq3PQ/ec//2H79u0OH//nn3/y4IMPEhQUxNixY2nVqpXXcsty1113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\n" + "text/plain": " BR GridEx GridREx CART COSMiK ExACT CREAM\n0 2491 10 10 10 4 6 6\n1 2492 10 10 10 8 6 6\n2 2493 10 10 10 9 6 7", + "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
BRGridExGridRExCARTCOSMiKExACTCREAM
02491101010466
12492101010866
22493101010967
\n
" }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": " index V model GridEx GridREx CART \\\n0 2016-03-04 00:00:00 410.0 518.750997 456.38289 441.865819 441.865819 \n1 2016-03-04 01:00:00 400.0 522.352090 456.38289 441.865819 441.865819 \n2 2016-03-04 02:00:00 395.0 525.129143 456.38289 441.865819 441.865819 \n3 2016-03-04 03:00:00 408.0 528.767307 456.38289 441.865819 441.865819 \n4 2016-03-04 04:00:00 406.0 528.091871 456.38289 441.865819 441.865819 \n.. ... ... ... ... ... ... \n643 2016-03-30 19:00:00 497.0 487.521085 456.38289 441.865819 441.865819 \n644 2016-03-30 20:00:00 501.0 489.614882 456.38289 441.865819 441.865819 \n645 2016-03-30 21:00:00 518.0 490.609540 456.38289 441.865819 441.865819 \n646 2016-03-30 22:00:00 510.0 491.321955 456.38289 441.865819 441.865819 \n647 2016-03-30 23:00:00 511.0 491.970557 456.38289 441.865819 441.865819 \n\n COSMiK CReEPy \n0 462.073299 477.108816 \n1 460.069181 473.640591 \n2 457.718672 469.171813 \n3 455.242336 465.418572 \n4 454.052186 462.959830 \n.. ... ... \n643 451.642998 432.796454 \n644 452.503446 433.519832 \n645 452.375362 432.264649 \n646 452.801142 431.704527 \n647 454.179640 433.203885 \n\n[648 rows x 8 columns]", - "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
indexVmodelGridExGridRExCARTCOSMiKCReEPy
02016-03-04 00:00:00410.0518.750997456.38289441.865819441.865819462.073299477.108816
12016-03-04 01:00:00400.0522.352090456.38289441.865819441.865819460.069181473.640591
22016-03-04 02:00:00395.0525.129143456.38289441.865819441.865819457.718672469.171813
32016-03-04 03:00:00408.0528.767307456.38289441.865819441.865819455.242336465.418572
42016-03-04 04:00:00406.0528.091871456.38289441.865819441.865819454.052186462.959830
...........................
6432016-03-30 19:00:00497.0487.521085456.38289441.865819441.865819451.642998432.796454
6442016-03-30 20:00:00501.0489.614882456.38289441.865819441.865819452.503446433.519832
6452016-03-30 21:00:00518.0490.609540456.38289441.865819441.865819452.375362432.264649
6462016-03-30 22:00:00510.0491.321955456.38289441.865819441.865819452.801142431.704527
6472016-03-30 23:00:00511.0491.970557456.38289441.865819441.865819454.179640433.203885
\n

648 rows × 8 columns

\n
" - }, - "execution_count": 24, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -275,11 +298,12 @@ "for e in extractors:\n", " print(abs(p.V - p[e]).mean(), abs(p.model - p[e]).mean())\n", "\n", - "plot(2491, p['index'], p.model, p.COSMiK)\n", - "plot(2491, p['index'], p.model, p.CReEPy)\n", - "plot(2491, p['index'], p.model, p.GridEx)\n", + "b = 2493\n", + "#plot(b, p['index'], p.model, p.COSMiK)\n", + "#plot(b, p['index'], p.model, p.ExACT)\n", + "#plot(b, p['index'], p.model, p.CREAM)\n", "\n", - "p" + "pd.DataFrame(rules)" ], "metadata": { "collapsed": false, @@ -290,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": null, "outputs": [], "source": [ "pd.DataFrame(predicted).to_csv(\"results/pred1.csv\")\n", @@ -306,7 +330,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "outputs": [], "source": [], "metadata": { @@ -347,17 +371,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "outputs": [ - { - "data": { - "text/plain": "V 454.232082\nmodel 454.416913\nCART2 55.474434\nCART5 39.939136\nCART10 37.642059\nCART25 36.371025\nCART50 39.203983\nCART75 40.454190\nCART100 43.945508\nCART150 47.059375\nCART200 49.097016\nCART250 51.038554\nCART300 51.182619\nCART400 52.651320\nCART500 52.874986\nCART600 53.924574\nName: mean, dtype: float64" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "outputs": [], "source": [ "import pandas as pd\n", "\n", From 7df13825a5ba0af24f7c4e73f2d4683b7d1ad295 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 2 Jul 2023 15:55:19 +0200 Subject: [PATCH 62/67] wip --- demo/DemoRegressionScaled.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/DemoRegressionScaled.ipynb b/demo/DemoRegressionScaled.ipynb index cf02cb48..133d3e91 100644 --- a/demo/DemoRegressionScaled.ipynb +++ b/demo/DemoRegressionScaled.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 38, + "execution_count": 1, "id": "6b710e7c", "metadata": {}, "outputs": [], @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 2, "outputs": [], "source": [ "def plot(testB, x, pred, extracted):\n", @@ -58,14 +58,14 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 5, "outputs": [], "source": [ "def splitTest(data):\n", " b = bartels[(bartels.n == 2490)]\n", " data = data[(data.index >= b.t1.values[0])]\n", "\n", - " b = bartels[(bartels.n == 2495) | (bartels.n == 2501) | (bartels.n == 2506)]\n", + " b = bartels[(bartels.n == 2494) | (bartels.n == 2495)]\n", " idx = np.zeros_like(data.index, dtype='bool')\n", " for _, row in b.iterrows():\n", " t0, t1 = row.t0, row.t1\n", @@ -163,7 +163,7 @@ " return CART.predict(test), CART.n_rules, sum([p is None for p in CART.predict(test)])\n", "\n", "def cosmik(model, train, test, normalization):\n", - " COSMiK = Extractor.cosmik(model, max_components=10, k=125, patience=12, close_to_center=True,\n", + " COSMiK = Extractor.cosmik(model, max_components=4, k=125, patience=12, close_to_center=True,\n", " output=Target.REGRESSION, normalization=normalization)\n", " COSMiK.extract(train)\n", " return COSMiK.brute_predict(test, 'default'), COSMiK.n_rules, sum([p is None for p in COSMiK.predict(test)])\n", From 55e5e7ed12fb13c68d60214abd7cc2b2e09a44aa Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Sun, 2 Jul 2023 16:27:26 +0200 Subject: [PATCH 63/67] wip --- psyke/extraction/hypercubic/hypercube.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 404430c5..9266b758 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -285,7 +285,8 @@ def remove_duplicates(points: Iterable[Point]) -> Iterable[Point]: def split(point: Point, feature: str, n: int): points = [] - for value in np.linspace(self.get_first(feature), self.get_second(feature), n): + a, b = self.get_first(feature), self.get_second(feature) + for value in np.linspace(a, b, n) if n > 1 else [(a + b) / 2]: new_point = point.copy() new_point[feature] = value points.append(new_point) From 529bf4349967e4d99ec7d3dc51f16a1f684c38ba Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 17 Jul 2023 04:07:59 +0200 Subject: [PATCH 64/67] [wip] --- psyke/extraction/hypercubic/__init__.py | 2 +- psyke/extraction/hypercubic/creepy/__init__.py | 3 ++- psyke/extraction/hypercubic/hypercube.py | 15 ++++++++------- psyke/utils/logic.py | 4 ++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 5becc59f..c0715076 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -122,7 +122,7 @@ def _ignore_dimensions(self) -> Iterable[str]: def __drop(self, dataframe: pd.DataFrame): self._hypercubes = [cube for cube in self._hypercubes if cube.count(dataframe) > 1] - def _create_theory(self, dataframe: pd.DataFrame, sort: bool = True) -> Theory: + def _create_theory(self, dataframe: pd.DataFrame, sort: bool = False) -> Theory: self.__drop(dataframe) new_theory = mutable_theory() for cube in self._hypercubes: diff --git a/psyke/extraction/hypercubic/creepy/__init__.py b/psyke/extraction/hypercubic/creepy/__init__.py index eb4a9584..1d72f759 100644 --- a/psyke/extraction/hypercubic/creepy/__init__.py +++ b/psyke/extraction/hypercubic/creepy/__init__.py @@ -11,6 +11,7 @@ from psyke.clustering import HyperCubeClustering from psyke.extraction.hypercubic import HyperCubeExtractor from psyke.utils import Target +from psyke.utils.logic import last_in_body class CReEPy(HyperCubeExtractor): @@ -40,7 +41,7 @@ def _extract(self, dataframe: pd.DataFrame, mapping: dict[str: int] = None, sort last_clause = list(theory.clauses)[-1] theory.retract(last_clause) theory.assertZ(clause( - last_clause.head, [list(last_clause.body)[-1]] if self._output is Target.REGRESSION else [])) + last_clause.head, [last_in_body(last_clause.body)] if self._output is Target.REGRESSION else [])) last_cube = self._hypercubes[-1] for dimension in last_cube.dimensions.keys(): last_cube[dimension] = [-np.inf, np.inf] diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index 9266b758..fcc2a063 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -379,14 +379,15 @@ def copy(self) -> RegressionCube: return RegressionCube(self.dimensions.copy()) def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, normalization=None) -> Iterable[Struct]: - intercept = self.output.intercept_ if normalization is None else \ - unscale(sum([-self.output.coef_[i] * normalization[name][0] / normalization[name][1] for i, name in - enumerate(self.dimensions.keys())], self.output.intercept_), list(normalization.keys())[-1]) - coefs = self.output.coef_ if normalization is None else \ - [self.output.coef_[i] / normalization[name][1] for i, name in enumerate(self.dimensions.keys())] + intercept = self.output.intercept_ if normalization is None else unscale(sum( + [-self.output.coef_[i] * normalization[name][0] / normalization[name][1] for i, name in + enumerate(self.dimensions.keys())], self.output.intercept_), list(normalization.keys())[-1]) + coefs = self.output.coef_ if normalization is None else [ + self.output.coef_[i] / normalization[name][1] * normalization[list(normalization.keys())[-1]][1] for + i, name in enumerate(self.dimensions.keys()) + ] return list(super().body(variables, ignore, unscale, normalization)) + [linear_function_creator( - list(variables.values()), [to_rounded_real(v) for v in coefs], - to_rounded_real(intercept) + list(variables.values()), [to_rounded_real(v) for v in coefs], to_rounded_real(intercept) )] diff --git a/psyke/utils/logic.py b/psyke/utils/logic.py index d606252d..e56da90a 100644 --- a/psyke/utils/logic.py +++ b/psyke/utils/logic.py @@ -134,6 +134,10 @@ def create_variable_list(features: list[DiscreteFeature], dataset: pd.DataFrame return values +def last_in_body(body: Struct) -> Struct: + return body.args[-1] if body.args[-1].functor == 'is' else last_in_body(body.args[-1]) + + def create_head(functor: str, variables: Iterable[Var], output) -> Struct: if isinstance(output, Var): variables += [output] From 235509a5aea871d0fe34e56904d1215c3365491a Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Wed, 12 Jul 2023 18:49:19 +0200 Subject: [PATCH 65/67] tests: [wip] --- psyke/extraction/hypercubic/__init__.py | 2 +- psyke/extraction/hypercubic/divine/__init__.py | 2 +- psyke/extraction/hypercubic/hypercube.py | 1 + psyke/utils/plot.py | 2 +- test/psyke/extraction/hypercubic/test_hypercube.py | 4 ++-- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index c0715076..12721d63 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -54,7 +54,7 @@ def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, def _create_brute_tree(self, criterion: str = 'center', n: int = 2) -> (BallTree, list[GenericCube]): points = None if criterion == 'center': - points = [(cube.center(), cube) for cube in self._hypercubes] + points = [(cube.center, cube) for cube in self._hypercubes] elif criterion == 'density': points = [(cube.barycenter, cube) for cube in self._hypercubes] elif criterion == 'corner': diff --git a/psyke/extraction/hypercubic/divine/__init__.py b/psyke/extraction/hypercubic/divine/__init__.py index d5674776..08c80e74 100644 --- a/psyke/extraction/hypercubic/divine/__init__.py +++ b/psyke/extraction/hypercubic/divine/__init__.py @@ -50,7 +50,7 @@ def __closest(self, data: pd.DataFrame, cube: GenericCube) -> (Point, pd.DataFra @staticmethod def closest_to_center(tree: BallTree, cube: GenericCube): - return tree.query([list(cube.center().dimensions.values())], k=1)[1][0][-1] + return tree.query([list(cube.center.dimensions.values())], k=1)[1][0][-1] @staticmethod def closest_to_corners(tree: BallTree, cube: GenericCube): diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index fcc2a063..b11643ba 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -259,6 +259,7 @@ def diagonal(self) -> float: lambda a, b: a + b, [(dimension[1] - dimension[0]) ** 2 for dimension in self._dimensions.values()], 0 ) ** 0.5 + @property def center(self) -> Point: return Point(list(self._dimensions.keys()), [(interval[0] + interval[1]) / 2 for interval in self._dimensions.values()]) diff --git a/psyke/utils/plot.py b/psyke/utils/plot.py index 04be6b96..fafbd346 100644 --- a/psyke/utils/plot.py +++ b/psyke/utils/plot.py @@ -60,7 +60,7 @@ def plot_perimeters(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[ def plot_centers(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): for cube in extractor._hypercubes: - center = cube.center() + center = cube.center plt.scatter(center[x], center[y], c=colors[cube.output], marker=m, edgecolor=ec, s=s, zorder=z, linewidth=lw) diff --git a/test/psyke/extraction/hypercubic/test_hypercube.py b/test/psyke/extraction/hypercubic/test_hypercube.py index b241029c..ef4fcec4 100644 --- a/test/psyke/extraction/hypercubic/test_hypercube.py +++ b/test/psyke/extraction/hypercubic/test_hypercube.py @@ -225,8 +225,8 @@ def test_diagonal(self): self.assertEqual(self.cube.diagonal(), ((self.x[1] - self.x[0])**2 + (self.y[1] - self.y[0])**2)**0.5) def test_center(self): - self.assertEqual(self.cube.center(), Point(list(self.dimensions.keys()), - [(val[0] + val[1]) / 2 for val in self.dimensions.values()])) + self.assertEqual(self.cube.center, Point(list(self.dimensions.keys()), + [(val[0] + val[1]) / 2 for val in self.dimensions.values()])) def test_corners(self): self.assertEqual(self.cube.corners(), [ From ba714e82f4cd03d79c976bd67a2ffea643e1091b Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Mon, 17 Jul 2023 04:17:38 +0200 Subject: [PATCH 66/67] chore: fix requirements --- requirements.txt | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/requirements.txt b/requirements.txt index ceedb00f..91d49f9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ build==0.10.0 twine==4.0.2 numpy==1.23.5 -<<<<<<< HEAD pandas==2.0.3 scikit-learn==1.2.2 2ppy==0.4.0 @@ -13,17 +12,3 @@ protobuf==4.23.4 setuptools==68.0.0 kneed==0.8.5 sympy==1.12 -======= -pandas==2.0.2 -scikit-learn>=1.1.1 -2ppy==0.4.0 -skl2onnx==1.14.1 -onnxruntime==1.14.1 -tensorflow>=2.7.2 -parameterized==0.9.0 -protobuf==3.20.3 -setuptools==67.8.0 -kneed==0.8.3 -sympy==1.12 -matplotlib>=3.7.1 ->>>>>>> c548b86dbcf073c41a9a48f4af3d989a53ed7fce From 9987e6f778d34a5406a8b79afdb5e114aed4fa67 Mon Sep 17 00:00:00 2001 From: Federico Sabbatini Date: Wed, 20 Sep 2023 17:22:34 +0200 Subject: [PATCH 67/67] feat(HyperCubeExtractor): surface-based brute prediction --- psyke/extraction/hypercubic/__init__.py | 31 +++++++++++++++--------- psyke/extraction/hypercubic/hypercube.py | 11 +++++++++ psyke/utils/plot.py | 6 +++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/psyke/extraction/hypercubic/__init__.py b/psyke/extraction/hypercubic/__init__.py index 12721d63..7a5ee046 100644 --- a/psyke/extraction/hypercubic/__init__.py +++ b/psyke/extraction/hypercubic/__init__.py @@ -38,6 +38,11 @@ def _brute_predict(self, dataframe: pd.DataFrame, criterion: str = 'corner', n: predictions[idx] = np.array([HyperCubePredictor._get_cube_output( self._surrounding, row ) for _, row in dataframe[idx].iterrows()]) + elif criterion == 'surface': + if not isinstance(self, HyperCubeExtractor): + raise ValueError("'surface' criterion only available for instances of HyperCubeExtractor") + predictions[idx] = np.array([HyperCubePredictor._get_cube_output(self._brute_predict_surface(row), row) + for _, row in dataframe[idx].iterrows()]) else: tree, cubes = self._create_brute_tree(criterion, n) predictions[idx] = np.array([HyperCubePredictor._brute_predict_from_cubes( @@ -51,21 +56,25 @@ def _brute_predict_from_cubes(row: dict[str, float], tree: BallTree, idx = tree.query([list(row.values())], k=1)[1][0][0] return HyperCubePredictor._get_cube_output(cubes[idx], row) + def _brute_predict_surface(self, row: dict[str, float]) -> GenericCube: + distances = [( + cube.surface_distance(Point(list(row.keys()), list(row.values))), cube.volume(), cube + ) for cube in self._hypercubes] + return min(distances)[-1] + def _create_brute_tree(self, criterion: str = 'center', n: int = 2) -> (BallTree, list[GenericCube]): - points = None - if criterion == 'center': - points = [(cube.center, cube) for cube in self._hypercubes] - elif criterion == 'density': - points = [(cube.barycenter, cube) for cube in self._hypercubes] - elif criterion == 'corner': - points = [(corner, cube) for cube in self._hypercubes for corner in cube.corners()] - elif criterion == 'perimeter': - points = [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] - else: + admissible_criteria = ['surface', 'center', 'corner', 'perimeter', 'density', 'default'] + if criterion not in admissible_criteria: raise NotImplementedError( - "'criterion' should be chosen in ['center', 'corner', 'perimeter', 'density', 'default']" + "'criterion' should be chosen in " + str(admissible_criteria) ) + points = [(cube.center, cube) for cube in self._hypercubes] if criterion == 'center' else \ + [(cube.barycenter, cube) for cube in self._hypercubes] if criterion == 'density' else \ + [(corner, cube) for cube in self._hypercubes for corner in cube.corners()] if criterion == 'corner' else \ + [(point, cube) for cube in self._hypercubes for point in cube.perimeter_samples(n)] \ + if criterion == 'perimeter' else None + return BallTree(pd.concat([point[0].to_dataframe() for point in points], ignore_index=True)), \ [point[1] for point in points] diff --git a/psyke/extraction/hypercubic/hypercube.py b/psyke/extraction/hypercubic/hypercube.py index b11643ba..342d1da7 100644 --- a/psyke/extraction/hypercubic/hypercube.py +++ b/psyke/extraction/hypercubic/hypercube.py @@ -269,6 +269,17 @@ def corners(self) -> Iterable[Point]: Point(list(self._dimensions.keys()), values) for values in itertools.product(*self._dimensions.values()) ] + def surface_distance(self, point: Point) -> float: + s = 0 + for d in self.dimensions.keys(): + lower, upper = self[d] + p = point[d] + if p > upper: + s += (p - upper)**2 + elif p < lower: + s += (lower - p)**2 + return s**0.5 + def perimeter_samples(self, n: int = 5) -> Iterable[Point]: def duplicate(point: Point, feature: str) -> Iterable[Point]: new_point_a = point.copy() diff --git a/psyke/utils/plot.py b/psyke/utils/plot.py index fafbd346..3d69f60f 100644 --- a/psyke/utils/plot.py +++ b/psyke/utils/plot.py @@ -50,6 +50,12 @@ def plot_boundaries(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[ fc='none', ec=colors[cube.output], alpha=a, hatch=h, linestyle=ls) +def plot_surfaces(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], ec='r', e=.05): + for cube in extractor._hypercubes: + plt.gca().fill_between((cube[x][0] - e, cube[x][1] + e), cube[y][0] - e, cube[y][1] + e, + fc='none', ec=ec) + + def plot_perimeters(extractor: HyperCubeExtractor, x: str, y: str, colors: dict[str, str], n: int = 5, ec: str = 'r', m: str = '*', s: int = 60, z: float = 1e10, lw: float = 0.8): for cube in extractor._hypercubes: