From 3a57c111967bd135020a7c5e7680274aeb569339 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 1 May 2024 19:41:16 +0200 Subject: [PATCH 01/28] classo exploration --- ci/recipe/meta.yaml | 1 + experiments/test_classo.ipynb | 555 ++++++++++++++++++++++++++++++++++ 2 files changed, 556 insertions(+) create mode 100644 experiments/test_classo.ipynb diff --git a/ci/recipe/meta.yaml b/ci/recipe/meta.yaml index dcc5543..9b95255 100644 --- a/ci/recipe/meta.yaml +++ b/ci/recipe/meta.yaml @@ -43,6 +43,7 @@ requirements: run_constrained: - pip: - coral_pytorch + - c-lasso test: diff --git a/experiments/test_classo.ipynb b/experiments/test_classo.ipynb new file mode 100644 index 0000000..dd5f480 --- /dev/null +++ b/experiments/test_classo.ipynb @@ -0,0 +1,555 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Test classo\n", + "code edited from CentralPark example in GitHub repos [here](https://github.com/Leo-Simpson/c-lasso/blob/master/examples/example_CentralParkSoil.py)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from os.path import join\n", + "from classo import classo_problem\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "%matplotlib inline\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = join(\"data\", \"CentralParkSoil\")\n", + "data = np.load(join(data_dir, \"cps.npz\"))\n", + "\n", + "# X are relative abundances\n", + "x = data[\"x\"] # (580, 3379)\n", + "\n", + "# y is target\n", + "y = data[\"y\"] # (580,)\n", + "\n", + "label = data[\"label\"] # (3704,) = 3379 OTUs + 325 nodes in tree\n", + "label_short = np.array([la.split(\"::\")[-1] for la in label])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# A is tree # todo: find out how to create this particular tree\n", + "# ! creation of A: my planned approach\n", + "# ! df_taxonomy = perform_taxonomic_classification()\n", + "# ! A = create_tree(df_taxonomy)\n", + "# ! create_tree should transform df with assignments to a 0,1 matrix:\n", + "# as in here: function \"phylo_to_A\":\n", + "# https://github.com/jacobbien/trac/blob/b6b9f4c08391d618152c4e02caf9eb4d6798aed8/R/getting_A.R#L64\n", + "A = np.load(join(data_dir, \"A.npy\")) # numpy array: (3379, 3704)\n", + "# 3704 = 3379 OTUs + 325 nodes in tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preprocess: taxonomy aggregation" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "pseudo_count = 1\n", + "X = np.log(pseudo_count + x)\n", + "nleaves = np.sum(A, axis=0)\n", + "logGeom = X.dot(A) / nleaves\n", + "\n", + "n, d = logGeom.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# define train set: tr\n", + "tr = np.random.permutation(n)[: int(0.8 * n)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cross validation and Path Computation" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + " \n", + "FORMULATION: R1\n", + " \n", + "MODEL SELECTION COMPUTED: \n", + " Cross Validation\n", + " \n", + "CROSS VALIDATION PARAMETERS: \n", + " numerical_method : not specified\n", + " one-SE method : True\n", + " Nsubset = 5\n", + " lamin = 0.001\n", + " Nlam = 80\n", + " with log-scale\n", + "\n" + ] + } + ], + "source": [ + "problem = classo_problem(logGeom[tr], y[tr], label=label_short)\n", + "\n", + "problem.formulation.w = 1 / nleaves\n", + "problem.formulation.intercept = True\n", + "problem.formulation.concomitant = False # not relevant for here\n", + "\n", + "# ! one form of model selection needs to be chosen\n", + "# stability selection: for pre-selected range of lambda find beta paths\n", + "problem.model_selection.StabSel = False\n", + "# calculate coefficients for a grid of lambdas\n", + "problem.model_selection.PATH = False\n", + "# todo: check if it is fair that trac is trained with CV internally whereas others are not\n", + "# lambda values checked with CV are `Nlam` points between 1 and `lamin`, with\n", + "# logarithm scale or not depending on `logscale`.\n", + "problem.model_selection.CV = True\n", + "problem.model_selection.CVparameters.seed = (\n", + " 6 # one could change logscale, Nsubset, oneSE\n", + ")\n", + "# 'one-standard-error' = select simplest model (largest lambda value) in CV\n", + "# whose CV score is within 1 stddev of best score\n", + "# ! create hyperparameter for this\n", + "problem.model_selection.CVparameters.oneSE = True\n", + "# ! create hyperparameter for this\n", + "problem.model_selection.CVparameters.Nlam = 80\n", + "# ! create hyperparameter for this\n", + "problem.model_selection.CVparameters.lamin = 0.001\n", + "\n", + "# ! for ritme: no feature_transformation to be used for trac\n", + "print(problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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x7Fjx9CoUSNdE6nSyp09exYvvPACJkyYgMjISLi6uuLtt9+Gubk51Go10tLScO/ePdjY2CA1NRUJCQmwsrLCsGHD0LZtWwwbNgwRERHl/OsREdVcclMHQEREplGnTh1s3LgRAwcOhEajgb29PbZs2YK6deviq6++wosvvggnJyf07NkTZmZmsLKyKnE9np6euqQhMTFRr2bgwoULWLNmDY4dO6abFh4ejpiYGBw/flzX+5KhcqmpqejcuTMA4Pz585g4cSIAQKPRQC6XY/r06Xjrrbfg5+eHPXv2ICAgACqVCr/++iusra3RoEGDcv3diIhqOokQQpg6CCIiqloyMzNha2sLAFi7di1Wr16Nv/76Szd/zJgx+Omnn9CjRw+88847+PTTT2Fvbw+lUomlS5catY2AgADs378fzs7OBsuNHDkS8+fPh5OTE3r37o0dO3YgOTkZc+bMwfLly7Fy5UqcP38e9evXx7Zt23DgwAGMGjUK48ePh5mZGb788kusXLny6X8MIiLSwwSCiIiK+eSTT7B161ao1Wo4OjpixYoVaNSokanDIiKiKoAJBBERERERGY0PURMRERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdH+H5KHPQ+d3iTvAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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imrVquHq1avo2bMnatasiQEDBhTbdvHixVp37K1SpQquXr2ql6xERERERC+Sp0309MzT1N69exenT5/WPM6cOaOXAd+3bt1C48aNcefOHZiYmEBEULt2bZw8ebJYD8bj/v3vf+P69etYvnz5U4/h5OTEAoOIiIiI6Bk87bt0mXowSmJra4uAgAAEBAQ86y5K5ODgAC8vL2zevBkjR45EcHAwnJ2dixUXqampqFWrFoyMjHD//n3s3r0bo0eP1msWIiIiqvicZ/6qdASi5ybps1eVjvBUZRrk/bwFBQUhKCgITZo0wWeffYZ169YBAMaMGYNffvkFABAcHIyWLVvCw8MD7du3R7du3fDWW28pGZuIiIiI6IX3zJdIVWa8RIqIiMhwsAeDXiQVoQfjad+lK2QPBhERERERVU7PXGBkZmbiwoUL+sxCRERERESVnE4FRvfu3ZGRkYGsrCx4eHigV69e+Oijj8orGxERERERVTI6FRg3b95E9erVsWfPHrz++uuIjY3FTz/9VE7RiIiIiIiostGpwMjLywMAHDlyBN26dYOpqWmxm+IREREREdGLS6cCo0WLFujevTt2796Nrl27Ijs7mwUGERERERFp6HSjvUWLFiE8PBweHh6wtLTEtWvXMG3atPLKRkRERERElYxOPRh9+vRBnz590KBBAwBAnTp1sGTJknIJRkRERERElU+ZejDy8/Px6NEjqNVq5OTkoPDefJmZmcjOzi7XgEREREREVHmUqQdj/vz5sLa2xvnz52FlZQVra2tYW1ujadOmGDp0aHlnJCIiIiKiSqJMBcbHH38MtVqNcePGQa1Wax4ZGRkIDAws74xERERERFRJ6DTIe+XKlVCr1bhx4wby8/M16+vVq6f3YEREREREVPnoVGB8++23eOedd2BiYgJjY2MAgEqlwq1bt8olHBERERERVS46FRhz587F6dOn4ebmVl55iIiIiIioEtNpmlp7e3sWF0REREREVCqdCoy+ffviq6++Qnp6OrKzszUPIiIiIiIiQMdLpGbOnAkAePfdd6FSqSAiUKlUKCgoKJdwRERERERUuehUYKjV6vLKQUREREREBkCnS6QA4NatWzh69CiA/93hm4iIiIiICNCxB2PHjh2YOnUqACApKQkXL17EBx98gD179pRLOEPmPPNXpSMQPTdJn72qdAQiIiJ6TnTq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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " CROSS VALIDATION : \n", + " Intercept : 5.9122282516799585\n", + " Selected variables : p__Bacteroidetes o__Acidobacteriales k__Bacteria \n", + " Running time : 12.524s\n", + "\n" + ] + } + ], + "source": [ + "problem.solve()\n", + "print(problem.solution)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Life::k__Bacteria::p__Bacteroidetes'\n", + " 'Life::k__Bacteria::p__Acidobacteria::c__Acidobacteriia::o__Acidobacteriales'\n", + " 'Life::k__Bacteria']\n" + ] + } + ], + "source": [ + "# ! class solution_CV: defined in @solver.py L930\n", + "selection = problem.solution.CV.selected_param[1:] # exclude the intercept\n", + "print(label[selection])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['__class__',\n", + " '__delattr__',\n", + " '__dict__',\n", + " '__dir__',\n", + " '__doc__',\n", + " '__eq__',\n", + " '__format__',\n", + " '__ge__',\n", + " '__getattribute__',\n", + " '__gt__',\n", + " '__hash__',\n", + " '__init__',\n", + " '__init_subclass__',\n", + " '__le__',\n", + " '__lt__',\n", + " '__module__',\n", + " '__ne__',\n", + " '__new__',\n", + " '__reduce__',\n", + " '__reduce_ex__',\n", + " '__repr__',\n", + " '__setattr__',\n", + " '__sizeof__',\n", + " '__str__',\n", + " '__subclasshook__',\n", + " '__weakref__',\n", + " 'beta',\n", + " 'formulation',\n", + " 'graphic',\n", + " 'index_1SE',\n", + " 'index_min',\n", + " 'label',\n", + " 'lambda_1SE',\n", + " 'lambda_min',\n", + " 'logscale',\n", + " 'refit',\n", + " 'save1',\n", + " 'save2',\n", + " 'selected_param',\n", + " 'standard_error',\n", + " 'time',\n", + " 'xGraph',\n", + " 'yGraph']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(problem.solution.CV)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.06649435996665047" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# selected lambda with 1-standard-error method\n", + "problem.solution.CV.lambda_1SE" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.0023974349678010775" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# selected lambda without 1-standard-error method\n", + "problem.solution.CV.lambda_min" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prediction plot" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3705" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# define test set\n", + "te = np.array([i for i in range(len(y)) if i not in tr])\n", + "\n", + "# alpha [0] is learned intercept, alpha [1:] are learned coefficients for all features\n", + "# in logGeom (n_samples, n_features)\n", + "# ! if oneSE=True -> uses lambda_1SE else lambda_min (see CV in\n", + "# ! classo>cross_validation.py)\n", + "# refit -> solves unconstrained least squares problem with selected lambda and\n", + "# variables\n", + "alpha = problem.solution.CV.refit\n", + "len(alpha)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB0KElEQVR4nO3dd1hT59sH8G8IQ4agIk5Q3Btw1FlXXa2tpaW2Kg7coyriqtW6956trVqLA7BaRauttk7cExVs9afWoqJiEbXgBEme94+8SdkkkORkfD/XxYWcnJxzhyjcPuO+ZUIIASIiIiIyezZSB0BERERE+sHEjoiIiMhCMLEjIiIishBM7IiIiIgsBBM7IiIiIgvBxI6IiIjIQjCxIyIiIrIQTOyIiIiILISt1AEUhlKpxIMHD1C0aFHIZDKpwyEiIiLSOyEEnj17hnLlysHGJu8xObNO7B48eAAvLy+pwyAiIiIyuPj4eHh6euZ5jlkndkWLFgWgeqGurq4SR0NERESkfykpKfDy8tLkPXkx68ROPf3q6urKxI6IiIgsmjbLzrh5goiIiMhCMLEjIiIishBM7IiIiIgshFmvsdOWQqHAmzdvpA6DyCrY2dlBLpdLHQYRkVWy6MROCIGHDx/i33//lToUIqtSrFgxlClThvUliYiMzKITO3VSV6pUKTg5OfGXDJGBCSHw8uVLJCYmAgDKli0rcURERNbFYhM7hUKhSerc3d2lDofIajg6OgIAEhMTUapUKU7LEhEZkcVunlCvqXNycpI4EiLro/53x7WtRETGZbGJnRqnX4mMj//uiIikYfGJHelP37598dFHHxn8Prt27ULVqlUhl8sREhKi9fOmT58OPz8/g8VlaFFRUZDJZNzsQ0REBcbEjkzOkCFD0LVrV8THx2PWrFk5niOTybBr1y6jxOPt7Y3ly5fr9Zpt2rTJlrQ2b94cCQkJcHNz0+u99C2n2MkwFAogKgrYskX1WaGQOiIiMnUWu3lCnxQK4PhxICEBKFsWaNkS4Hpww3j+/DkSExPRqVMnlCtXTupwjMre3h5lypSROgwyEZGRwKhRwL17/x3z9ARWrAACAqSLi4hMG0fs8hEZCXh7A23bAoGBqs/e3qrjhrJ9+3bUq1cPjo6OcHd3R/v27fHixQsAwPnz59GhQweULFkSbm5uaN26NS5evJjp+TKZDGvWrMEHH3wAJycn1KpVC6dPn8Zff/2FNm3awNnZGc2aNcOtW7c0z1FPY65ZswZeXl5wcnLCp59+mue0oBACCxcuROXKleHo6AhfX19s3749z9f29OlT9OnTB8WLF4eTkxPee+893Lx5E4BqKrJo0aIAgHfeeQcymQxRUVHZruHt7Q0A+PjjjyGTyTRfq23evBne3t5wc3ND9+7d8ezZswLH3KZNG9y5cwejR4+GTCbLtHbs1KlTaNWqFRwdHeHl5YXg4GDN+wQAq1evRrVq1VCkSBGULl0aXbt2BaCa0j569ChWrFihuebt27ezTcVu2LABxYoVw++//45atWrBxcUF7777LhISEjT3SE9PR3BwMIoVKwZ3d3dMmDABQUFBeU6Z37lzB126dEHx4sXh7OyMOnXqYO/evZrHr169is6dO8PFxQWlS5dG7969kZSUlGfspF+RkUDXrpmTOgC4f1913JA/f4jIzAkzlpycLACI5OTkbI+9evVKXL16Vbx69arA19+xQwiZTAgg84dMpvrYsaMw0efswYMHwtbWVixdulTExcWJ2NhY8c0334hnz54JIYQ4dOiQ2Lx5s7h69aq4evWqGDBggChdurRISUnRXAOAKF++vNi6dau4fv26+Oijj4S3t7d45513xG+//SauXr0qmjZtKt59913Nc6ZNmyacnZ3FO++8Iy5duiSOHj0qqlatKgIDAzXnBAUFCX9/f83XkyZNEjVr1hS//fabuHXrlggNDRUODg4iKioq19f34Ycfilq1aoljx46Jy5cvi06dOomqVauKtLQ0kZqaKq5fvy4AiB07doiEhASRmpqa7RqJiYkCgAgNDRUJCQkiMTFR8xpcXFxEQECAuHLlijh27JgoU6aMmDRpUoFjfvz4sfD09BQzZ84UCQkJIiEhQQghRGxsrHBxcRHLli0TN27cECdPnhT169cXffv2FUIIcf78eSGXy0VERIS4ffu2uHjxolixYoUQQoh///1XNGvWTAwaNEhzzfT0dHHkyBEBQDx9+lQIIURoaKiws7MT7du3F+fPnxfR0dGiVq1amd6T2bNnixIlSojIyEhx7do1MXToUOHq6prpfcrq/fffFx06dBCxsbHi1q1bYs+ePeLo0aNCCNXfv5IlS4qJEyeKa9euiYsXL4oOHTqItm3b5hl7Vvr492et0tOF8PTM/nMn488fLy/VeURkHfLKd7JiYpcLqX64RkdHCwDi9u3bWsaZLooWLSr27NmjOQZATJ48WfP16dOnBQCxfv16zbEtW7aIIkWKaL6eNm2akMvlIj4+XnNs3759wsbGRpPMZEzsnj9/LooUKSJOnTqVKZ4BAwaIHj165BjrjRs3BABx8uRJzbGkpCTh6Ogotm3bJoQQ4unTpwKAOHLkSJ6vG4DYuXNnpmPTpk0TTk5OmZLc8ePHiyZNmhQ4ZiGEqFixoli2bFmmY7179xaDBw/OdOz48ePCxsZGvHr1SuzYsUO4urpmiiWj1q1bi1GjRmU6llNiB0D89ddfmnO++eYbUbp0ac3XpUuXFosWLdJ8nZ6eLipUqJBnYlevXj0xffr0HB+bMmWK6NixY6Zj8fHxAoC4fv16rrFnxcSu4I4cyf3nTsaPfP6JEJGBKRQKsXz5cjF69GiD30uXxI5r7HJx/Hj2aZCMhADi41XntWmjv/v6+vqiXbt2qFevHjp16oSOHTuia9euKF68OABV0depU6fi8OHD+Oeff6BQKPDy5UvcvXs303V8fHw0fy5dujQAoF69epmOvX79GikpKXB1dQUAVKhQAZ6enppzmjVrBqVSievXr2db+3X16lW8fv0aHTp0yHQ8LS0N9evXz/G1Xbt2Dba2tmjSpInmmLu7O2rUqIFr165p/T3Ki7e3t2Y6F1B1PlB3QShIzLmJjo7GX3/9hfDwcM0xIQSUSiXi4uLQoUMHVKxYEZUrV8a7776Ld999Fx9//LHOdRWdnJxQpUqVHF9PcnIy/vnnHzRu3FjzuFwuR8OGDaFUKnO9ZnBwMIYNG4b9+/ejffv2+OSTTzR/X6Kjo3HkyBG4uLhke96tW7dQvXp1neIn3WWYadfLeUSkfwkJCejXrx9+//13AMCnn36KZs2aSRyVChO7XEj1w1Uul+PAgQM4deoU9u/fj1WrVuGrr77C2bNnUalSJfTt2xePHj3C8uXLUbFiRTg4OKBZs2ZIS0vLdB07OzvNn9XrwnI6llcCoD4np5pk6uf9+uuvKF++fKbHHBwccryeECLX4/qqe5bxNQKq2NWxFiTm3CiVSgwZMgTBwcHZHqtQoQLs7e1x8eJFREVFYf/+/Zg6dSqmT5+O8+fPo1ixYoV6PVm/j1m/d7l9n9UGDhyITp064ddff8X+/fsxb948LFmyBCNHjoRSqUSXLl2wYMGCbM9jezDj0PbbzLeDSBq7d+/GgAEDkJSUhCJFimDJkiVo2rSp1GFpcPNELqT84SqTydCiRQvMmDEDly5dgr29PXbu3AkAOH78OIKDg9G5c2fUqVMHDg4OmoXthXX37l08ePBA8/Xp06dhY2OT4yhN7dq14eDggLt376Jq1aqZPry8vHK8fu3atZGeno6zZ89qjj1+/Bg3btxArVq1dIrVzs4OCh1rPxQkZkC1WzXrvRo0aIA///wz23WqVq0Ke3t7AICtrS3at2+PhQsXIjY2Frdv38bhw4dzvaau3NzcULp0aZw7d05zTKFQ4NKlS/k+18vLC0OHDkVkZCTGjh2LdevWZXpd3t7e2V6Xs7Oz3mKn3LVsqdr9mtv/dWQywMtLdR4RGc/Lly8xbNgw+Pv7IykpCb6+voiOjsbnn39uUkXZOWKXC/UP1/v3VdOuWclkqsf1/cP17NmzOHToEDp27IhSpUrh7NmzePTokSbxqVq1KjZv3oxGjRohJSUF48eP1/TmLKwiRYogKCgIixcvRkpKCoKDg/HZZ5/lWIKjaNGiGDduHEaPHg2lUom3334bKSkpOHXqFFxcXBAUFJTtOdWqVYO/vz8GDRqENWvWoGjRovjyyy9Rvnx5+Pv76xSrt7c3Dh06hBYtWsDBwUEzVZ2XgsSsvtexY8fQvXt3ODg4oGTJkpgwYQKaNm2K4cOHY9CgQXB2dsa1a9dw4MABrFq1Cr/88gv+/vtvtGrVCsWLF8fevXuhVCpRo0YNzTXPnj2L27dvw8XFBSVKlNDp9auNHDkS8+bNQ9WqVVGzZk2sWrUKT58+zfOHTEhICN577z1Ur14dT58+xeHDhzV/v4YPH45169ahR48eGD9+PEqWLIm//voLP/74I9atWwe5XJ5j7DY2/D+ivsjlqpImXbuqfs5k/PmjfluXL2fJJSJjunTpEgIDA/G///0PADB27FjMmTNH59keozDscj/DMtau2Kw7Yw25K/bq1auiU6dOwsPDQzg4OIjq1auLVatWaR6/ePGiaNSokXBwcBDVqlUTP/30U7bF/ciysSAuLk4AEJcuXdIcy7pQf9q0acLX11esXr1alCtXThQpUkQEBASIJ0+eaJ6TdVesUqkUK1asEDVq1BB2dnbCw8NDdOrUSbPDMidPnjwRvXv3Fm5ubsLR0VF06tRJ3LhxQ/O4tpsndu/eLapWrSpsbW1FxYoVM72GjJYtW6Z5vKAxnz59Wvj4+AgHBweR8Z/MuXPnRIcOHYSLi4twdnYWPj4+Ys6cOUII1UaK1q1bi+LFiwtHR0fh4+Mjtm7dqnnu9evXRdOmTYWjo6MAIOLi4nLcPOHm5pYplp07d2aK4c2bN2LEiBHC1dVVFC9eXEyYMEF8+umnonv37rm+nhEjRogqVaoIBwcH4eHhIXr37i2SkpI0j9+4cUN8/PHHolixYsLR0VHUrFlThISECKVSmWvsWXHzROHt2JF9A5eXl2F+7hBRzhQKhVi0aJGws7MTAETZsmXF/v37jR6HLpsnZELksyDHhKWkpMDNzQ3JycmaDQBqr1+/RlxcHCpVqoQiRYoU+B45FQn18lL9j9mSioROnz4du3btwuXLl6UOhQpBqVSiVq1a+Oyzz3Lt2mEM+vr3Z+1YHJ1IOvfv30dQUBAOHToEAPjoo4+wbt06lCxZ0uix5JXvZMWp2HwEBAD+/vzhSqbpzp072L9/P1q3bo3U1FR8/fXXiIuLQ2BgoNShkR7I5frddU9E2omMjMSgQYPw5MkTODk5Yfny5Rg4cKBJraXLDRM7LfCHK5kqGxsbbNiwAePGjYMQAnXr1sXBgwd13oxCRESqtpYhISFYv349AKBhw4YIDw/XrI82B5yKJSK9478/IjI358+fR8+ePXHz5k3IZDJMmDABM2bM0FQ6kBKnYomIiIi0oFAosHDhQkydOhXp6enw9PTE5s2b0cZMp+qY2BEREZFVunv3Lnr37o1jx44BUHWQWLNmjVYltEwVi08RERGR1dm6dSt8fHxw7NgxuLi4IDQ0FFu3bjXrpA7giB0RERFZkZSUFIwcORKbNm0CADRp0gRhYWGoWrWqxJHpB0fsiIiIyCqcPn0afn5+2LRpE2xsbDBlyhQcP37cYpI6gCN2REREZOHS09MxZ84czJo1CwqFAhUrVkRYWBjefvttqUPTO47YmaA2bdogJCQk18e9vb2xfPlyg8cRFRUFmUyGf//91+D3IiIiMgR13+7p06dDoVCgZ8+eiImJscikDmBiZ5IiIyON3g4qp2SyefPmSEhIgJubGwBgw4YNKFasmFHjIiIiKgghBDZv3gw/Pz+cPn0arq6uCAsLQ1hYmOb3miXiVKwJKlGihNQhAADs7e1RpkwZqcMgIiLSyb///othw4bhxx9/BAC0aNECYWFh8Pb2ljYwI+CInQnKOHqWmJiILl26wNHREZUqVUJ4eHi285OTkzF48GCUKlUKrq6ueOeddxATE6N5fPr06fDz88PmzZvh7e0NNzc3dO/eHc+ePQMA9O3bF0ePHsWKFSsgk8kgk8lw+/btTFOxUVFR6NevH5KTkzXnTJ8+HTNnzkS9evWyxdSwYUNMnTrVMN8gIiKiXBw7dgy+vr748ccfIZfLMWvWLERFRVlFUgcwsTN5ffv2xe3bt3H48GFs374dq1evRmJiouZxIQTef/99PHz4EHv37kV0dDQaNGiAdu3a4cmTJ5rzbt26hV27duGXX37BL7/8gqNHj2L+/PkAgBUrVqBZs2YYNGgQEhISkJCQAC8vr0xxNG/eHMuXL4erq6vmnHHjxqF///64evUqzp8/rzk3NjYWly5dQt++fQ37zSEiIvp/b968wVdffYU2bdrg7t27qFKlCk6ePInJkyfD1tZ6Jiit55VClQS9fPnS6Pd1cnKCTCbT+Xk3btzAvn37cObMGTRp0gQAsH79+kwN3o8cOYIrV64gMTERDg4OAIDFixdj165d2L59OwYPHgwAUCqV2LBhA4oWLQoA6N27Nw4dOoQ5c+bAzc0N9vb2cHJyynXq1d7eHm5ubpDJZJnOcXFxQadOnRAaGoq33noLABAaGorWrVujcuXKOr9mIiIiXd28eRM9e/bUDDL069cPK1as0PzOsyZWldi9fPkSLi4uRr/v8+fP4ezsrPPzrl27BltbWzRq1EhzrGbNmpk2MERHR+P58+dwd3fP9NxXr17h1q1bmq+9vb0z/QUvW7ZsppG/whg0aBD69++PpUuXQi6XIzw8HEuWLNHLtYmIiHIjhEBoaCiCg4Px4sULFCtWDGvXrsWnn34qdWiSsarEztwIIQAgz9E+pVKJsmXLIioqKttjGRNAOzu7TI/JZDIolUq9xNmlSxc4ODhg586dcHBwQGpqKj755BO9XJuIiCgnT548weDBg7Fjxw4AqvXpmzZtyraUyNpYVWLn5OSE58+fS3LfgqhVqxbS09Nx4cIFNG7cGABw/fr1THXlGjRogIcPH8LW1rZQC0Pt7e2hUCgKdI6trS2CgoIQGhoKBwcHdO/evcCvmYiIKD+HDx9Gnz59cP/+fdja2mLOnDkYO3Ys5HK51KFJzqoSO5lMVqApUanUqFED7777LgYNGoS1a9fC1tYWISEhcHR01JzTvn17NGvWDB999BEWLFiAGjVq4MGDB9i7dy8++uijTNO4efH29sbZs2dx+/ZtuLi45FhyxdvbG8+fP8ehQ4fg6+sLJycnTQI3cOBAzdq/kydP6uHVExERZZaWlobJkydj8eLFEEKgevXqiIiIQMOGDaUOzWRwV6yJCw0NhZeXF1q3bo2AgABNWRM1mUyGvXv3olWrVujfvz+qV6+O7t274/bt2yhdurTW9xk3bhzkcjlq164NDw8P3L17N9s5zZs3x9ChQ9GtWzd4eHhg4cKFmseqVauG5s2bo0aNGpqNHkRERPryv//9D02bNsWiRYsghMDgwYNx8eJFJnVZyIR6IZcZSklJgZubG5KTk+Hq6prpsdevXyMuLg6VKlVCkSJFJIrQegghULNmTQwZMgRjxoyROhySGP/9EZG+CCGwZs0ajBkzBq9evYK7uzu+//57fPTRR1KHZjR55TtZSTpil56ejsmTJ6NSpUpwdHRE5cqVMXPmTL0t6ifjSExMxNKlS3H//n3069dP6nCIiMhCPHr0CP7+/hg2bBhevXqFDh06IDY21qqSOl1JusZuwYIF+O6777Bx40bUqVMHFy5cQL9+/eDm5oZRo0ZJGRrpoHTp0ihZsiTWrl2L4sWLSx0OERFZgN9//x19+/bFw4cPYW9vj/nz52PUqFGwseEqsrxImtidPn0a/v7+eP/99wGoFudv2bIFFy5ckDIs0pEZz+YTEZGJef36NSZOnIjly5cDAGrXro2IiAj4+vpKG5iZkDTtffvtt3Ho0CHcuHEDABATE4MTJ06gc+fOOZ6fmpqKlJSUTB9ERERkGf744w80btxYk9SNGDECFy5cYFKnA0lH7CZMmIDk5GTUrFkTcrkcCoUCc+bMQY8ePXI8f968eZgxY4aRoyQiIiJDEkLg66+/xvjx45GamopSpUrhhx9+0MzokfYkHbHbunUrwsLCEBERgYsXL2Ljxo1YvHgxNm7cmOP5EydORHJysuYjPj4+33twmpDI+Pjvjoi09fDhQ3Tu3BnBwcFITU3Fe++9h9jYWCZ1BSTpiN348ePx5Zdfonv37gCAevXq4c6dO5g3bx6CgoKyne/g4KBpdJ8fdQutly9fZiroS0SG9/LlSwDZW9kREWX0yy+/oH///nj06BEcHBywePFiDB8+PM9WmpQ3SRO7ly9fZtvdIpfL9VLuRC6Xo1ixYppG905OTvyLQmRgQgi8fPkSiYmJKFasGNv7EFGOXr58ifHjx2P16tUAAB8fH0RERKBOnToSR2b+JE3sunTpgjlz5qBChQqoU6cOLl26hKVLl6J///56uX6ZMmUAQJPcEZFxFCtWTPPvj4goo8uXLyMwMBDXrl0DAIwePRpz585lMXM9kbTzxLNnzzBlyhTs3LkTiYmJKFeuHHr06IGpU6fC3t4+3+drW4lZoVDgzZs3+gydiHJhZ2fHkToiykapVGLZsmWYOHEi3rx5gzJlymDjxo3o2LGj1KGZPF06T1hsSzEiIiIyDQ8ePEBQUBAOHjwIAPD398f333+PkiVLShyZeTCblmJERERk2Xbu3Il69erh4MGDcHR0xJo1a7Bz504mdQYi6Ro7IiIiskwvXrzA6NGjsW7dOgBAgwYNEB4ejpo1a0ocmWXjiB0RERHp1YULF9CgQQOsW7cOMpkMEyZMwOnTp5nUGQFH7IiIiEgvFAoFFi1ahClTpiA9PR3ly5fH5s2b0bZtW6lDsxpM7IiIiKjQ4uPj0bt3bxw9ehQA8Mknn2Dt2rUoUaKExJFZF07FEhERUaFs27YNPj4+OHr0KJydnfHDDz/gp59+YlInAY7YERERUYE8e/YMI0eO1PR4b9y4McLDw1G1alWJI7NeHLEjIiIinZ05cwZ+fn7YuHEjbGxsMHnyZJw4cYJJncQ4YkdEpGcKBXD8OJCQAJQtC7RsCbAZB1mK9PR0zJ07FzNnzoRCoUCFChUQFhaGli1bSh0agYkdEZFeRUYCo0YB9+79d8zTE1ixAggIkC4uIn2Ii4tDr169cOrUKQBAjx49sHr1ahQrVkzawEiDU7FERHoSGQl07Zo5qQOA+/dVxyMjpYmLSB/CwsLg6+uLU6dOoWjRoggLC0NERASTOhPDxI6ISA8UCtVIXU7dt9XHQkJU5xGZk3///Rc9e/ZE79698ezZMzRv3hwxMTHo2bOn1KFRDpjYERHpwfHj2UfqMhICiI9XnUdkLo4fPw4/Pz9ERERALpdj5syZOHr0KCpVqiR1aJQLrrEjItKDhAT9nkckpTdv3mDmzJmYO3culEolKleujPDwcDRt2lTq0CgfTOyIiPSgbFn9nkcklb/++gs9e/bEuXPnAABBQUFYtWoVihYtKnFkpA1OxRIR6UHLlqrdrzJZzo/LZICXl+o8IlMkhEBoaCj8/Pxw7tw5FCtWDD/++CM2bNjApM6MMLEjItIDuVxV0gTIntypv16+nPXsyDQ9efIE3bp1Q//+/fHixQu0bt0aMTEx6Natm9ShkY6Y2BER6UlAALB9O1C+fObjnp6q46xjR6boyJEj8PX1xU8//QRbW1vMmzcPhw4dQoUKFaQOjQqAa+yIiPQoIADw99eu8wQ7VJCU0tLSMHXqVCxcuBBCCFSrVg0RERFo1KiR1KFRITCxIyLSM7kcaNMm73PYoYKkdP36dQQGBuLixYsAgIEDB2LZsmVwcXGRODIqLE7FEhEZGTtUkFSEEFi7di3q16+PixcvokSJEtixYwfWrVvHpM5CMLEjIjIidqggqSQlJeHjjz/GkCFD8OrVK7Rr1w6xsbEI4BCxRWFiR0RkROxQQVLYv38/6tWrh59//hl2dnZYvHgx9u/fj/JZd/qQ2eMaOyIiI2KHCjKm169fY9KkSVi2bBkAoFatWoiIiICfn5+0gZHBMLEjIjIidqggY/nzzz8RGBiI2NhYAMDnn3+ORYsWwcnJSeLIyJA4FUtEZETsUEGGJoTA119/jUaNGiE2NhYeHh7Ys2cPvvnmGyZ1VoCJHRGREbFDBRnSP//8gw8++AAjR47E69ev8e677yI2NhYffPCB1KGRkTCxIyIyMnaoIEP49ddfUa9ePezduxcODg5YuXIl9u7dizJlykgdGhkR19gREUlAlw4VRHl59eoVxo8fj2+++QYAUK9ePURERKBu3boSR0ZSYGJHRCQRbTpUEOUlJiYGgYGBuHr1KgAgJCQE8+bNQ5EiRSSOjKTCqVgiIiIzo1QqsWzZMjRu3BhXr15FmTJl8Ntvv2HZsmVM6qwcR+yIiIjMyIMHD9C3b18cOHAAAPDhhx/i+++/h4eHh8SRkSngiB0REZGZ2LVrF3x8fHDgwAE4Ojriu+++w65du5jUkQZH7IiIiEzcixcvMGbMGKxduxYA4Ofnh4iICNSqVUviyMjUcMSOiIjIhEVHR6NBgwaapG78+PE4c+YMkzrKEUfsiIiITJBCocDixYsxefJkpKeno1y5cti0aRPatWsndWhkwpjYERERmZj4+Hj06dMHUVFRAICAgACsXbsW7u7u0gZGJo+JHRGRmVMoWOjYkvz0008YMmQInj59CmdnZ6xcuRL9+vWDLLcGw0QZMLEjIpPGpCVvkZHAqFHAvXv/HfP0VPWjZWsy8/Ls2TOMGjUKoaGhAIC33noL4eHhqFatmsSRkTnh5gkiMlmRkYC3N9C2LRAYqPrs7a06TqrvQ9eumZM6ALh/X3Wc3yfzcfbsWdSvXx+hoaGQyWSYNGkSTp48yaSOdMbEjohMEpOWvCkUqpE6IbI/pj4WEqI6j0yXQqHA7Nmz0aJFC9y6dQteXl6IiorCnDlzYGdnJ3V4ZIaY2BGRyWHSkr/jx7MnvRkJAcTHq84j03T79m20adMGU6ZMgUKhQLdu3RAbG4tWrVpJHRqZMSZ2RGRymLTkLyFBv+eRcUVERMDX1xcnTpxA0aJFsWnTJmzZsgXFihWTOjQyc9w8QUQmh0lL/sqW1e95ZBzJyckYPnw4wsPDAQDNmjVDWFgYKleuLHFkZCk4YkdEJodJS/5atlTtfs2tAoZMBnh5qc4j03DixAn4+voiPDwcNjY2mD59Oo4dO8akjvSKiR0RmRwmLfmTy1UlTYDs3yf118uXszSMKXjz5g2mTp2K1q1b486dO6hUqRKOHz+OadOmwdaWE2ekX0zsiMjkMGnRTkAAsH07UL585uOenqrjrGMnvVu3bqFly5aYNWsWlEol+vTpg8uXL6N58+ZSh0YWSiZETvvOzENKSgrc3NyQnJwMV1dXqcMhIj3Lqfiul5cqqWPS8h8WcTY9Qghs2rQJI0aMwPPnz+Hm5obvvvsO3bt3lzo0MkO65DtM7IjIpFlT0mJNr9WSPX36FEOHDsW2bdsAAK1atcLmzZtRoUIFiSMjc6VLvsPJfSIyaXI50KaN1FEYHluDWYaoqCj07t0b9+7dg62tLWbMmIEJEyZAzgydjIRr7IiIJMYuG+YvLS0NEydOxDvvvIN79+6hatWqOHnyJCZNmsSkjoyKiR0RkYTYZcP8Xb9+Hc2bN8f8+fMhhMCAAQNw6dIlNG7cWOrQyAoxsSMiklBhu2woFEBUFLBli+ozE0DjEUJg3bp1aNCgAaKjo1G8eHFs374d33//PVxcXKQOj6wU19gREUmoMF02uC5POklJSRg0aBB27doFAHjnnXewceNGeHp6ShsYWT2O2BERSaigXTa4Lk86Bw4cgI+PD3bt2gU7OzssWrQIBw4cYFJHJoGJHRGRhArSZYPr8qSRmpqKsWPHomPHjkhISEDNmjVx9uxZjBs3DjY2/HVKpoF/E4mIJFSQLhuFXZdHurt69SqaNGmCpUuXAgCGDRuG6Oho1K9fX+LIiDJjYkdEJDFdW4MVZl0e6UYIgdWrV6Nhw4aIiYlByZIlsXv3bqxevRpOTk5Sh0eUDTdPEBGZgIAAwN9fu84TBV2XR7pJTExE//798euvvwIAOnXqhA0bNqBMmTISR0aUOyZ2REQmQtsuG+p1effv57zOTiZTPZ5xXR7pZt++fejbty8SExPh4OCAhQsXYsSIEVxLRyaPf0OJiMxMQdblkXZevXqF4OBgdO7cGYmJiahbty7Onz+P4OBgJnVkFvi3lIjIDOm6Lo/yFxsbi7feegurVq0CAAQHB+PcuXOoV6+exJERaY9TsUREJkyhyH3dnS7r8ih3SqUSK1euxIQJE5CWlobSpUsjNDQU7733ntShEemMiR0RkYnSprOEtuvyKGcJCQno168ffv/9dwDABx98gPXr16NUqVISR0ZUMJyKJSIyQewsYXi7d++Gj48Pfv/9dxQpUgSrV6/G7t27mdSRWWNiR2Tm2ATe8rCzhGG9fPkSw4YNg7+/P5KSkuDr64vo6GgMGzYMstxagBCZCSZ2RGYsMhLw9gbatgUCA1Wfvb05mmPu2FnCcC5evIgGDRrgu+++AwCMHTsWZ8+eRe3atSWOjEg/mNgRmSlO1VkudpbQP6VSiUWLFqFp06a4fv06ypYtiwMHDmDx4sVwcHCQOjwiveHmCSIzlN9UnUymmqrz9+cOSXOkbceIq1eBQ4dUf05M5K7Y3Ny7dw9BQUE4fPgwAODjjz/GunXr4O7uLnFkRPonEyKnXw3mISUlBW5ubkhOToarq6vU4RAZTVSUato1P0eOcMekOVIoVFPquXWWyEvWXbPWbseOHRg0aBCePn0KJycnrFixAgMGDOBaOjIruuQ7nIolMkOcqrNseXWWyA+n4lWeP3+OAQMGoGvXrnj69CkaNmyIS5cuYeDAgUzqyKIxsSMyQ2wCb/ly6yyRH+6aBc6dO4f69evjhx9+gEwmw8SJE3Hq1ClUr15d6tCIDI6JHZEZUjeBz23gQSYDvLzYBN7cBQQAt2+rptQnT9b+eda6a1ahUGDOnDlo3rw5/vrrL3h5eeHIkSOYO3cu7O3tpQ6PyCiY2BGZITaBtx7qzhIFqcZhTVPxd+7cQdu2bTF58mQoFAp89tlniImJQevWraUOjciomNgRmSkpmsCzGLJ0CjKtbi1T8Vu2bIGvry+OHz8OFxcXbNy4ET/++COKFy8udWhERsddsURmLq8m8fqkTd9SMhxddsrKZKr3Ji7Oskdtk5OTMWLECISFhQEAmjZtirCwMFSpUkXiyIj0y2x2xXp7e0Mmk2X7GD58uJRhEZkV9VRdjx6qz4ZK6lgMWVra7pS1lqn4kydPws/PD2FhYbCxscG0adNw/PhxJnVk9SQdsXv06BEUGeZy/vjjD3To0AFHjhxBGy2Kb3HEjig7fY/gqUeKcmtxZS2jQ/qk7XuU9bzmzYH581UJ3pMnOV/by0uV1FnqKGp6ejpmzZqF2bNnQ6lUwtvbG2FhYWjRooXUoREZjC75jqSdJzw8PDJ9PX/+fFSpUoWLXYkKyBDTpbr0LWUx5Pxp+x7ldJ5cnnldY4kSwMiRqsTQGjpP/P333+jZsyfOnDkDAOjduzdWrVoFNzc3iSMjMh0ms3kiLS0NYWFh6N+/f67FI1NTU5GSkpLpg4hUDDVdau7FkE1pw4e271Fu52WN/elTYOZMIDnZsFPxUhNCYNOmTfDz88OZM2fg6uqKiIgIbNq0iUkdURYmk9jt2rUL//77L/r27ZvrOfPmzYObm5vmw8vLy3gBEpmw/HrHAgUvWGvOxZAjI1XTyG3bAoGBqs/e3tKsCdT2PUpLy/283J43apSqZ6wpJK/69vTpU/To0QNBQUF49uwZ3n77bcTExKBHjx5Sh0ZkkkxmV2ynTp1gb2+PPXv25HpOamoqUlNTNV+npKTAy8uLa+zI6hmyd2x+uzELusbO0Lt51aNeWWNWTwgYqiRMbrR9j4YPB775pnD3spTdykePHkXv3r0RHx8PuVyOGTNm4Msvv4TcEoclifJgNrti1e7cuYODBw9i4MCBeZ7n4OAAV1fXTB9EZNjpUkMUQzb0SJohRzALStvvfWGTOkCVhH/yiWqa1hxH8d68eYOvvvoKbdu2RXx8PKpUqYKTJ0/iq6++YlJHlA+TSOxCQ0NRqlQpvP/++1KHQmSWDD1dqs9iyMYonaLLhg9jMeZUtTp5nTZN+iloXd28eRPNmzfH3LlzIYRA//79cenSJTRp0kTq0IjMguSJnVKpRGhoKIKCgmBrK+kmXSKzZYzesRn7lkZEqD7HxemW1BlrJM0UN3zk9x4ZmqnXHBRCYP369fDz88OFCxdQvHhx/PTTT1i/fj2KFi0qdXhEZkPyxO7gwYO4e/cu+vfvL3UoRGbLWL1jC1sM2Vgjaaa44UPbAsOGItUUtDYeP36Mrl27YuDAgXj58iXatm2L2NhYdO3aVerQiMyO5Ildx44dIYRA9erVpQ6FyKxJ0TtWV8YaSTPGCGZB5PYeGYsUU9D5OXToEHx8fBAZGQk7OzssWLAABw4cgKenp9ShEZklzn0SWZCAAMDf3zi9YwvCWCNp6tGxrl1VSVzGqV+pW25lfY8ePADGjTNuDKZQczA1NRWTJ0/G4sWLAQA1atRAeHg4GjZsKHFkROZN8hE7ItIvY/SOLShjjqSZ8gim+j1ycACWLjX+/aWuOXjt2jU0bdpUk9QNGTIE0dHRTOqI9IAjdkRkNMYeSTPlEczc6uwVRMmSQFJS/uepaw4aewpaTQiB7777DmPGjMHr16/h7u6O9evXw9/fX5qAiCwQEzsiMir1SFpO/VIN0bxePTpmSvLaHawrd3fVjtdTp1TJ682bwPTpqsdMaQo6MTERAwYMwC+//AJAtb56w4YNKCv18CGRhWFiR0RGZ8ojacaQ3+5gXXz7LWBvnzl5rVvXeImzNn777Tf07dsX//zzD+zt7bFgwQIEBwfDxoargYj0jYkdEUnCFEfSjEWfmxc8PLK3Z/P3N43E+fXr15gwYQJWrlwJAKhTpw4iIiLg4+Nj3ECIrAgTOyIiI9Pn7OPPPwO9e2cfnZO6V+yVK1cQGBiIP/74AwAwcuRILFiwAI6OjtIFRWQFZELoY5WHNHRpiktEZCoUClWLr/v39bPOLiv1ejopdv8KIbBy5UpMmDABqampKFWqFEJDQ9G5c2fjBkJkQXTJd7jAgYjISBQKICoK2LYNGDRIldQVpgtFblOrUnWZePjwITp37oyQkBCkpqaic+fOuHLlCpM6IiPiVCwRkRFERmbf0ODurvr8+HHBrplX0paxy4Qx1jLu2bMH/fv3R1JSEooUKYLFixfj888/h0yq5rhEVoojdkREBqauWZd1J+yTJ6qPGTOAiAhg2TLtrufhoRqN04ahu0y8fPkSn3/+OT788EMkJSXBx8cHFy5cwPDhw5nUEUmAiR0RkQHlVbNOfez774HPPgNGjsy7MwegSuru3VPtetWGIcvEXbp0CQ0bNsS3334LABgzZgzOnTuHOnXqGO6mRJQnJnZERAaUX826jFOm6s4cQPbkTiZTfXz3napunTHbs2WlVCqxePFiNGnSBP/73/9QtmxZ7N+/H0uWLIGDg4P+b0hEWmNiR0RkQNpOharPy6/Hrb9/5g0YQM5JIGCYLhP3799Hx44dMX78eLx58wb+/v6IjY1Fhw4d9HsjIioQbp4gIjIgbadCM56XW2eOn39WlUnJbwOGobpMREZGYtCgQXjy5AmcnJywfPlyDBw4kGvpiEwIEzsiIgNST5nmVrNOJlM9nnXKNGtnDvUGjKzXePJEdWzGDKBaNcN0mXj+/DlCQkKwfv16AEDDhg0RHh6OGjVq6O8mRKQXTOyIiAxIvW6ua1dVEpcxMctvylTdKuz+fdUu2Nw2YMhkqg0YcXH6n3o9f/48evbsiZs3b0Imk2HChAmYMWMG7O3t9XsjItILrrEjIjKw/NbN5TRlGhmpmnZt2xbo1QtISsr9+hk3YOiLQqHAvHnz0Lx5c9y8eROenp44fPgw5s2bx6SOyIRxxI6IyAhyWzeX0whbbtOu+dFXzbq7d++id+/eOHbsGADg008/xZo1a1C8eHH93ICIDIaJHRGRkWRdN5eTtDRg6NCC9ZDVR826rVu3YsiQIUhOToaLiwtWrVqFoKAgbpAgMhOciiUiMhGRkarp2kePdH+uh0fhatalpKQgKCgI3bt3R3JyMpo0aYJLly6hb9++TOqIzAhH7IiITEBBp1/VAgO1m+bNyenTp9GzZ0/ExcXBxsYGX331FaZMmQI7O7uCBUNEkpEJUdAfI9JLSUmBm5sbkpOT4erqKnU4REQFolBkr0+nq5IlM2+w8PRU7cbNq5Zdeno65syZg1mzZkGhUKBixYoICwvD22+/XfBAiEjvdMl3OBVLRCSx/NqOaSPrrtn791UjgJGROZ8fFxeHVq1aYfr06VAoFAgMDERMTAyTOiIzx8SOiDJRKFQtq7ZsUX1WKKSOyPLpazdrRuq5mJCQzO+hEAJhYWHw9fXF6dOn4erqirCwMISHh8PNzU3/gRCRUTGxIyKNjLXTAgNVn729cx/1If3Qx27WnGStb/fvv/+iZ8+e6N27N549e4YWLVrg8uXL6Nmzp2ECICKjY2JHRAD+W7yfdUowvyk9KryWLYESJQx3/YQE4Pjx4/D19cWWLVsgl8sxc+ZMREVFoVKlSoa7MREZHRM7IoJCAYwalXvLKiD7lB7pj1wOfPihoa7+Bnv3TkabNm1w9+5dVK5cGSdOnMCUKVNga8vCCESWhokdEeW7eN8QLassXX5rFbM+/s47hojiJuztWyAsbA6USiX69u2Ly5cvo2nTpoa4GRGZAP53jYi0XrxviEX+ligyUjUCmjFZzlh+JKfHPTz0GYEAEAogGGlpL1CsWDGsWbMGn332mT5vQkQmiCN2RKT14n1DLfK3JPmtVfzii5wfz1quRFteXsD48arEUeUJgE8BDADwAq1bt0ZsbCyTOiIrwQLFRKQpkHv/fs7r7GQyVeIQF6d9NwNt71vQbgmmKL9CwzIZYGNT+LWKHh5Az56Av/9/3zOFAlix4jDmzOmDJ0/uw9bWFrNnz8a4ceMgN+dvKhHplO9wKpaIIJerpgm7dlUlHxmTO3Wb0OXL9Zt05TddaY60WauoTVKXUxeJQYOAatVyToDT0tIwefJkLF68GEIIVK9eHREREWjYsGHBXwwRmSUmdkQEQJVMbd+ec7K1fLl+k63c+qKqpyu3bzfP5E5faxCXLwfKl9duJPN///sfAgMDcenSJQDA4MGDsXTpUjg7O+snGCIyK5yKJaJMDD09qs10pSGmfY0hKkpV1LmwjhwB2rTJ+xwhBNasWYMxY8bg1atXcHd3x/fff4+PPvqo8AEQkUnhVCwRFZhcnn9SURi6lFbJGIc5rMdr2VKVlOa1VjGvNXbqpLZly7zv8+jRIwwYMAB79uwBAHTo0AEbNmxAuXLlCvkKiMjccVcsERlVQUqrmEurM/VaReC/tYlq6q/HjFH9ObfH81vL+Pvvv8PHxwd79uyBvb09li5dil9//Q03bpRjf18iYmJHRPqRX0FeNV1Lq5hbqzP1WsXy5TMf9/RUHV+4MO/Hc1tb+Pr1a4wePRrvvvsuHj58iPLla+Obb87By2s0Kle2Mfmkl4iMg2vsiKjQdNnhqktpFcB81+PlN3Wsy9TyH3/8gcDAQFy5cuX/j4wAsBCAY47nq0f/zHUTChFlpku+w8SOiAoltx2ueSUX6ucAOZdWUT9H280I2mw2MEdCCHz99dcYP348UlNTAXhA1VHi/Xyfa8pJLxHpRpd8h1OxRFRgCoVqpC6n/x6qj4WEZJ+WzW+6Up0IWnOrs3/++Qfvv/8+goODkZqaiiJF3gNwBdokdQD7+xJZKyZ2RFRguuxwzSogALh9WzXaFhGh+hwXl3l0z5xbnWm75jAnv/76K+rVq4d9+/bBwcEBwcGr8Pr1rwBK6xyHJSa9RJQ7nRO76dOn486dO4aIhYjMTGFH1NSlVXr0UH3OOmWoLh+SdQepmkym6pWaX3kQYyvoLt5Xr15hxIgR+OCDD/Do0SPUq1cPFy5cQNOmIwDk8k3IhykmvURkODondnv27EGVKlXQrl07RERE4PXr14aIi4jMgKFH1LQpH6LvVmeFVdBdvNHRl1GrVkN88803AIBRo0bj3LlzqFu3boG+f6aa9BKRYemc2EVHR+PixYvw8fHB6NGjUbZsWQwbNgznz583RHxEZMIy9jPNTWGTC23X45mCgqw5VCqV6Nt3KRo1aoI7d64BKAPgN+zYsRR79xYBkP/IZVammvQSkeEValdseno69uzZg9DQUPz222+oUaMGBg4ciL59+8LNzU2fceaIu2KJpJNfazC1bduATz/Vz/1MvfOErrt4Hzx4gPfeC0Js7MH/f+RDAN8D8IBMpkoGQ0IAf3/g0SOgWzfVWfn91Pby0n9/XyKSjtF2xSqVSqSlpSE1NRVCCJQoUQLffvstvLy8sHXr1sJcmohMXH4bJ9Q8PPRzv/zW45kCbdccHjoEjBmzEzVr1vv/pM4RwHcAdkFV0uS/5G35clWyOGYMMG5c9pFLLy9V8pzXJhQish4F6hUbHR2N0NBQbNmyBQ4ODujTpw+++eYbVK1aFQCwZMkSBAcHo5v6v5dEZHGsuRRJbm7e1OasF5g9ezSAdf//dX0AEQBq5vms+/eBxYtVSVzJkqY9cklE0tE5sfPx8cG1a9fQsWNHrF+/Hl26dIE8y0+VPn36YPz48XoLkohMjzmXIjGEyEhg+vT8zroAoCeAG1Dtch0PYBYA+3yvL4Rq7dyYMSw6TES50zmx+/TTT9G/f3+UzzofkIGHhweUSmWhAiMi06Ze0J9fazBL3pWpXvd3/75qLVzua98UABYBmAIgHUB5AJsAvKPT/TLWBbTEThtEVHg6J3ZTpkwxRBxEZGbUpUi6doVmob+aNezKzKk/bs7iAfQGcPT/v/4EwFoAJQp8b2ua3iYi3bDzBBEVmKmWIilM1wdt5FarLrttAHygSuqcAfwA4CcUJqkDrGd6m4h0V6hyJ1JjuRMi02BKpUhyGknz9FSNLuoj0dSuzMszACMBbPz/rxsDCAdQtVD3Vk9vc40dkXXRJd8p0K5YIqKM1KVIpKYeScv631V11wd9jCLmX+blDFQbJP6GalJkEoCpkMvtCjVyaA3T20RUeJyKJSKLUJCuDwWR+/q2dAAzAbwNVVJXAUAUZLJZkMnsMGaMKjnTtntEVlJPbxORedBqxC42NlbrC/r4+BQ4GCKigspvJE1fO0pzXt8WB6AXgFP//3V3AN8CKAZPz/+6QDRtqu2Gi8wmT1aVUuFIHRHlR6vEzs/PDzKZDLktx1M/JpPJoND3KmUiIi0Yq2By9jIv4QA+B5ACoCiA1ShZsieWL5ehfPnM6w0DAlTtwdTrEf/5Bxg9Ov97tmvHpI6ItKNVYhcXF2foOIiICsVYBZPlclVbs0WLkqFK6CL+/5HmAMIgk1XCmjW5T5lmXI+oUABLllh3LUAi0i+tEruKFSsaOg4ikoAp7WYtLGMVTI6MBBYtOgHV1OsdAHIAU6HaJGELZ2ftr2XttQCJSP8KXO7k6tWruHv3LtLS0jId//DDD/USmDZY7oSo4AxdFkQK6l2xQM5JUsbNBwVJal+/foNSpWbi2bO5AJQAKkE1Fdss27k7dmj/fczpvfDy+m9tHhFZN13yHZ0Tu7///hsff/wxrly5kmndnez/f3Iac40dEzuigsmtLEhOCZC50SZJKkhS+9dff6FLl5743//O/f+RIAArAeT8s8fLS7d6c5Y0ekpE+mXQxK5Lly6Qy+VYt24dKleujHPnzuHx48cYO3YsFi9ejJZGXAzCxI5Id/kV2LWEIrh5JUm6JrVCCGzYsAEjR47EixcvALgBWAOgW75xHDmSeT0dEzciKgiDFig+ffo0Dh8+DA8PD9jY2MDGxgZvv/025s2bh+DgYFy6dKnAgROR4RmrLIiUciuYnF+tO5lMVevO3191jSdPnmDo0KH46aefAAA+Pq0QG7sZqhp1+VPvwLXEaW8iMk06FyhWKBRwcXEBAJQsWRIPHjwAoNpgcf36df1GR0R6Z6yyIKZIl6T2yJEj8PX1xU8//QRbW1vMmzcP588fhqendkkdoBqZy62vrLobRmRkAV8MEVEOdB6xq1u3LmJjY1G5cmU0adIECxcuhL29PdauXYvKlSsbIkYi0iNjlQUxRdolq2lYunQqfvllIYQQqFatGiIiItCoUSMA/+1izWsRi3o6u3lzoEoV7UcIiYgKS+cRu8mTJ0OpVAIAZs+ejTt37qBly5bYu3cvVq5cqfcAiUi/1GVBcmttJZOpFv5bYu20/JPV6wCaYc+eBRBCYODAgbh48aImqQNUU6fbtwPu7jlfIWOZklOntB8hJCLSB50Tu06dOiHg/xeFVK5cGVevXkVSUhISExPxzjvv6D1AItIvde00IHtyZ+m103JPagWAtQDqA7iIEiVKYMeOHVi3bp1m6UlGAQGqrhEzZgAlSmR+zNMT2LZNdXzHDu3iym0kUaEAoqKALVtUn9nYh4jyU+A6dn/99Rdu3bqFVq1awdHRUdNSzJi4K5ao4Ky1dlr2WndJAAYC+BkAUK9eO+zbtxHly5fX6npZd7s+egSMGaNbP9iMu2czxskNF0QEGLjcyePHj/HZZ5/hyJEjkMlkuHnzJipXrowBAwagWLFiWLJkSaGC1wUTO6LCsdYSHP8lTfuhqkf3EIAdgoLm4YcfRsPGRufJDM1181t/l5VcrhqR+/TT/K9jCXUGiUh3uuQ7Ov/0Gj16NOzs7HD37l04OTlpjnfr1g2//fab7tESkWTUZUF69FB9toakDgA6d36NTz4ZA6ATgIeoUKEmLlw4hw0bxhY4qcurlEp+z+vW7b/dsfmVZAFUGy44LUtEOdH5J9j+/fuxYMECeHp6ZjperVo13LlzR2+BEREZwp9//okmTZpgxYplAIBhw4bh2rVoNGzoV6jr5ldKJT/qZE2XkixERFnpnNi9ePEi00idWlJSEhwcHPQSFBGRvgkh8PXXX6NRo0aIjY1FyZIlsXv3bqxevTrHn2m6Kkzdv4zJmjXXGSSiwtM5sWvVqhU2bdqk+Vomk0GpVGLRokVo27atXoMjskbcCal///zzDz744AOMHDkSr1+/RqdOnXDlyhV06dJFb/fQR90/9VpHY92PiCyPzgWKFy1ahDZt2uDChQtIS0vDF198gT///BNPnjzByZMnDREjkdXgTkj927t3L/r164fExEQ4ODhg4cKFGDFiRIHX0uVGXUrl/n3d19mpqTew5HUddfFjS6wzSESFp/NPttq1ayM2NhaNGzdGhw4d8OLFCwQEBODSpUuoUqWKIWIksgpsPaVfr169wsiRI/H+++8jMTERdevWxfnz5xEcHKz3pA7Iuz5gfjIWhbbmOoNEVHg6lTt58+YNOnbsiDVr1qB69eqGjEsrLHdClkKhALy9c180rx6liYvjL3RtxMTEIDAwEFevXgUAjBo1CvPnz0eRIkUMfu/c6gN27w4sXqz6OuNP3dxKmFhrnUEiys5g5U7s7Ozwxx9/6LUQ8f3799GrVy+4u7vDyckJfn5+iI6O1tv1icwBd0Lqh1KpxLJly9C4cWNcvXoVpUuXxr59+7B8+XKjJHWAKum6fVtVdDgiQvU5Lg5YuFCVvGWte1yypCqBK1Ei83rK3K7DpI6I8qJzgeKxY8fCzs4O8+fPL/TNnz59ivr166Nt27YYNmwYSpUqhVu3bsHb21uraV2O2JGl2LIFCAzM/7yICFXNOcruwYMH6Nu3Lw4cOAAA6NKlC9avXw8PD49cnyNFgWb1PX/+GQgLA5KS/nuM6ymJKCe65Ds6b55IS0vD999/jwMHDqBRo0ZwdnbO9PjSpUu1vtaCBQvg5eWF0NBQzTFvb29dQyIye9wJWTi7du3CwIED8fjxYzg6OmLp0qUYMmRInrMLhdmoUpiEUC4HnjxR3Sfrf6vV6ynz6ixhrd1CiEg7Oo/Y5VXSRCaT4fDhw1pfq3bt2ujUqRPu3buHo0ePonz58vj8888xaNAgrZ7PETuyFOo1dnnthCxZEli2TDWVx1/mKi9evMCYMWOwdu1aAICfnx8iIiJQq1atPJ9XmJZdhd25XJj1lNw1TWSddMp3hIQcHByEg4ODmDhxorh48aL47rvvRJEiRcTGjRtzPP/169ciOTlZ8xEfHy8AiOTkZCNHTqR/O3YIIZOpPlQpR+4fnp6q863ZhQsXRPXq1QUAAUCMHz9evH79Ot/npaervn+5fW9lMiG8vFTnZaV+j3J6jkym3Xty5Ej+7y+gOk/f9yYi85ScnKx1viNpYmdnZyeaNWuW6djIkSNF06ZNczx/2rRpmh/iGT+Y2JGl2LEj76SDv8yFSE9PF/Pnzxe2trYCgChXrpw4ePCg1s8vaGJVmIQwo4gI7e4fEaG61pEjQoSFCeHhUfh7E5F50iWx038xJx2ULVsWtWvXznSsVq1auHv3bo7nT5w4EcnJyZqP+Ph4Y4RJZDTqnZAHDwKTJgFFi+Z8nrU2g4+Pj0f79u3x5ZdfIj09HQEBAYiNjUW7du20vkZBW3bpa+eytuskb95UTdm2bQv06gU8elT4exOR5dN584Q+tWjRAtevX8907MaNG6hYsWKO5zs4OLAfLVm8n3/Ovo4qJxl/mbdpY5TQJPXTTz9hyJAhePr0KZydnbFy5Ur069cv3/JLWTcblCql3f2yJmD66uGqTWeJEiWAadO0u19BYiQiyyVpYjd69Gg0b94cc+fOxWeffYZz585h7dq1moXQRNYmt0X9edH1l7m57ap89uwZRo0apdk9/9ZbbyE8PBzVqlXL97m5bTZwd1ftTNWlZZe+di6rO0t07aq6V07FiguKu6aJSNI1dkIIsWfPHlG3bl3h4OAgatasKdauXav1c3WZcyYydfmt4dJ2LVheclrDZ8obMc6cOSOqVKkiAAiZTCYmTZok0tLStHpuXpsNcvpzfmsX1e9PbptbdF3nltN74eUlxIwZuv8d4Bo7IsumS76jc7kTU8JyJ2RJoqJU66m0pWubscKU+DA2hUKBefPmYfr06VAoFPDy8sLmzZvRunVrLZ+ff0mREiWAIkVUU6Jq+bXsUn8PgZxH2nT9HuY0erptm3bFqgt7byIyHwYtUExEhqHLlKquzeAVCtWUZE7/jRNCdb2QEMDfX/pp2du3b6N37944ceIEAKBbt2749ttvUbx4ca2voc1Gh8ePVZtU5HLtp6UDAlQJVE7TuwXp4SqXZ18fqet0akHvTUSWiYkdkYnQ5Re6rr/MddnRKeVGjIiICAwbNgwpKSkoWrQovvnmG/Tq1Uvn/tTaJsmJiaoWbeqRs23b8k/wAgJUCbCh1inmt7kCADw8WKyaiHLGxI7IRGjzC93dHdi6VZV86fLLXF87Og0lOTkZI0aMQFhYGACgWbNmCAsLQ+XKlQt0PV02OhSkm0NOI205KchGFW02V3z3HUfoiChnktaxI6L/qH+hA9l3R8pkqo+1a4F27XQfoTHlXrQnT56En58fwsLCYGNjg2nTpuHYsWMFTuqA/5Lk3Ab6ZDLVerqkJFUClXU0U92zNTKywCEgMvK/OnSBgarP3t7aXVM95Vu+fObjnp5cS0dEeePmCSITk9MIknpRf0GnAPPbTKC+h7YbMfQhPT0ds2bNwuzZs6FUKuHt7Y3w8HA0b95cL9fPb6PD1q3AmDEF69mq7b0Lu1HF3ErTEJFh6JLvcMSOyMSou08cOQJERKg+x8WpHivoCJBcrlpLlpfu3Y2XNNy6dQstW7bEzJkzoVQq0bt3b8TExOgtqQPyH/Xy8NBPJ4ms8tuoAmjfMUQ95dujh+7T70RknThiR2QGCjsCZCojdkIIbNq0CSNGjMDz58/h5uaGb7/9Fj3yyzoLIbdRry1btCsrEhGRf1KckbZla44csY6OIURUeCx3QmRB9FGqJL9dsYDhd8U+ffoUQ4cOxbZt2wAALVu2xObNm3NtIagvuW10MNS6Q1PfqEJElo1TsUQmTh/N56VONqKiouDj44Nt27bB1tYWc+bMwZEjRwye1OVF2w0WWVuL5ceUN6oQkeVjYkdk4vSRlEmVbKSlpWHixIl45513cO/ePVStWhUnT57EpEmTIJd4wVh+u5AB7QtAZ2SohJGISBtM7IhMnD6SMimSjevXr6N58+aYP38+hBAYMGAALl26hMaNG+vvJoVkiLIihkoYiYi0wc0TRCZOvfEht8LF2pbl0Hef09wIIfD9998jJCQEL1++RPHixbFu3Tp88sknhb+4gRS2rEhOz//559zL1rAOHRHpgpsniCyINp0ItBkB0nef05wkJSVh0KBB2LVrFwDgnXfewcaNG+Hp6Vn4ixuQtp0kcpJX54rbt1mHjoiMiyN2RGYir8LFuiRlhip6e+DAAQQFBSEhIQF2dnaYM2cOxo4dCxsby13xoa9CxEREedEl32FiR2RGTLETQWpqKiZNmoSlS5cCAGrUqIGIiAg0aNBA2sAMLL/agIXpXEFElBGnYoksVGGmDA3h6tWrCAwMRExMDABg6NChWLJkCZycnCSOLGf6TIx1KUNjSu8ZEVk2y50jISKDEUJg9erVaNiwIWJiYlCyZEn8/PPP+Pbbb002qYuMLHhLtpxIXRuQiCgnTOyISCeJiYno0qULhg8fjtevX6Njx46IjY3Fhx9+KHVouVKvhcs6wnb/vup4QZI7FiImIlPExI6ItLZv3z7Uq1cPv/76K+zt7bF8+XLs27cPZU04e8mvJRugasmmUOh2XRYiJiJTxMSOiPL16tUrBAcHo3PnzkhMTESdOnVw/vx5jBo1yuR3veqjJVtOWIiYiEyRaf9EJiLJXblyBY0bN8aqVasAAMHBwTh//jx8fHwkjkw7hlwLV5jOFQoFEBUFbNmi+qzriCERUU64K5aIcqRUKrFq1SpMmDABqampKF26NEJDQ/Hee+9JHZpODL0WLiAA8PfXbbdtXkWNWfeOiAqDdeyIKJuEhAT069cPv//+OwDggw8+wPr161GqVCmtnm9K9fb01ZJNX1jUmIh0pUu+w6lYIspk9+7d8PHxwe+//44iRYpg9erV2L17t9ZJnb7LihSWKa2FM9RGDiIiNSZ2RAQAePnyJYYNGwZ/f38kJSXB19cX0dHRGDZsGGS5bf3MwhBlRfShMGvh9MlQGzmIiNS4xo6IcPHiRQQGBuL69esAgLFjx2LOnDmwtXVAVJR2U6r5jUbJZKrRKH9/aaZlC7IWTt9Y1JiIDI2JHZEVUyqVWLJkCb766iu8efMGZcuWxaZNm9C+fXudF/ibQ4stqVuysagxERkap2KJrNS9e/fQoUMHfPHFF3jz5g0+/vhjXLlyRZPU6TqlytGo/LGoMREZGhM7Iiu0Y8cO+Pj44PDhw3BycsK6deuwY8cOuLu7F3iBP0ej8mdKGzmIyDIxsSOyIs+fP8eAAQPQtWtXPH36FA0bNsTFixcxcOBAzQaJgi7w52iUdkxlIwcRWSausSOyEufOnUPPnj3x119/QSaTYcKECZgxYwbs7e0znVfQKVX1aFTXrqokLuOInyFGo0ypVp6uTGEjBxFZJiZ2RBZOoVBg/vz5mDZtGhQKBTw9PbF582a0yWUXQWGmVNWjUTltuli+XH+jUZbQuUHqjRxEZJnYeYLIgt25cwe9e/fG8f+fN/3000+xZs0aFC9ePNfn6KNTgyFH09i5gYisjS75DhM7Igv1448/YujQoUhOToaLiwu+/vpr9OnTR6tiw+rkCch5SlWq5EmddOa2BtDY7cEKy5ynk4nIeNhSjMiKpaSkoE+fPujRoweSk5PRpEkTXL58GUFBQVp3kDDVBf6W1LnB1FqvEZFl4Bo7Igty6tQp9OrVC3FxcbCxscHkyZMxefJk2NnZ6XwtU1zgbym18nKbTlbXCeR0MhEVFBM7IguQnp6O2bNnY9asWVAqlfD29kZYWBhatGhRqOua2gJ/S6iVZ+qt14jIvHEqlsjM/f3332jVqhVmzJgBpVKJXr164fLly4VO6kyRJdTKs6TpZCIyPUzsiMyUEAKbNm2Cn58fTp8+DVdXV4SHh2Pz5s1wc3OTOjyDsITODZYynUxEpomJHZEZevr0KXr06IGgoCA8e/YMb7/9NmJiYhAYGCh1aAZnqhs7tGUJ08lEZLpY7oTIzBw9ehS9e/dGfHw85HI5ZsyYgS+//BJyAwxTmXI5DlOOLS/6qBNIRNZFl3yHmyeIzMSbN28wffp0zJs3D0IIVKlSBeHh4WjSpIlB7mfq3R1MbWOHtozdeo2IrAunYonMwM2bN9G8eXPMnTsXQgj0798fly5dMmhS17Vr9kX+6nIckZGqkaeoKGDLFtVnhcIgoVgkc59OJiLTxalYIhMmhMAPP/yA4OBgvHz5EsWLF8fatWvRVd0WwgC06e5QogRQpIgq0VMzpdE8c2Gu08lEZFyciiWyAI8fP8bgwYMR+f+tCNq0aYNNmzbBy8vLoPfVphzH48fZj1tKcV1jJlvmOp1MRKaLiR2RCciaTKSlHUK/fn3w4MED2NraYs6cORg7dqxBNkhkVdAyG5ZQXNfU1xUSEeWHiR2RxDInE6kAJgNYDACoXr06IiIi0LBhQ6PFU5gyGxmL65rbSBTbfBGRJeDmCSIJZd6kcA1AU6iTOmAIpk27aNSkDsi/u4M2zK24bn5tvgDVSCQ3iBCRqWNiRySR/5IJAeBbAA0AXAbgDmAXZLLv8OWXzkZPJvLq7qAtcyuuyzZfRGQpmNgRSUSVTDwC4A/gcwCvAXQAEAvAX9JkIq9yHO7u5t2rNSds80VEloJr7Igk8ttvvwHoC+AfAPYA5gMYhaz/39JnMqHLjs+AANUmiKzn//yz5RXXZZsvIrIUTOyIjOz169f48ssvsUI934naACIA+OZ4vr6SiYLs+MypHId6NC/rtYoXVx3z99dPvMakXleYX5svcxuJJCLrw6lYIiO6cuUK3nrrrQxJ3QgAF5BbUqevaU1tOknoIiAAuH0bmDFDVawYAJ48AaZNUxU31vV6UstrXaE5j0QSkfVhYkdkBEIIrFy5Em+99Rb++OMPlCpVCu7uvwJYBcAx1+ctXVr4ZMJQOz5//hmYPl2V0GVU0GRRamzzRUSWgC3FyOyZalsmdVzXrj3Ehg39cO7cbwCAzp07Y8CAH/DJJ6XzvcaRI6rXU5jXFxUFtG2b/3lHjmhfe06btmOenkBcnGm8F7ow1b9PRGS92FKMrIapdgr4L649APoDSAJQBAMHLsbatZ/jxx+1qyPy889A796Fe32G2PGpS3kQcytUzDZfRGTOOBVLZkvf68b0Gdcnn7zEvXufA/gQqqTOB8AFrF8/HDt3yrTeELF8eeFfn7b3unlTu/MAlgchIjJVTOzILJlqpwCFAhg27BKAhlAVHQaAMQDOAaijiat587y7O8hkuU//6fr6tO0kMW2a/pNFlgchIjIuJnZklkyxU4BSqcSIEYuRmNgEwP8AlAWwH8ASAA6Z4jp1Ku9dmELknbTp8voy7vjMi0ymv2TRXAsVExGZOyZ2ZJZMbSrw/v376NixI777bjyAN1B1k4iFqpNEznHltQszJES7+2r7+gICVDtY81LQZDG/8iAKhWoDx5Ytqs/st0pEZDhM7MgsmdJUYGRkJHx8fHDo0CE4ODgCWANgJ4CS+calrgd35AgQEaH6HBenfZFfXV5ftWranadLsphfeZDISNXu2bZtgcBA1WdzrHNHRGQuWO6EzJK63EZ+nQIMWW7j+fPnCAkJwfr16wEADRo0wKZN4Xj33ZqFjssQr88QZU/UsUZFqT4A1XPbtPmv9VjW+NUjetZUG44lVIioMHTKd4QZS05OFgBEcnKy1KGQBHbsEEImU32o0gfVh/rYjh2Gu/e5c+dEtWrVBAAhk8nEhAkTRGpqql7j0vfrS08XwtMz+/UyXtfLS3WeLnbsUF0347XKlxfC3T3n+xTmXuYop++Pp6dh/34SkWXRJd9hYkdmLadfml5ehvulmZ6eLubOnStsbW0FAFG+fHlx+PBhg8Wl79en72RRfb3cErj8Po4cKdjrMBe5fX+M8Z8PIrIcuuQ7nIols2esaa67d++id+/eOHbsGACga9euWLNmDUqom6UaKC59v76cijp7eak2O+gyNZpf9wltREQAPXoU/PmmzJK7cxCRcemS7zCxI9LC1q1bMWTIECQnJ8PZ2Rlff/01goKCIMuvOJyJ0keyqO2avbzoup7PnBhqTSMRWR+2FCPSk5SUFIwcORKbNm0CADRu3Bjh4eGoWrWqxJEVjj7aZhWmlIx6tMqS69yZWkkeIrIOLHdClIvTp0+jfv362LRpE2xsbDBlyhScOHHC7JM6fSloKZmsde4slSmV5CEi68HEjiiL9PR0zJw5Ey1btsTff/+NihUr4ujRo5g5cybs7OykDs9kaNN9wt1ddU5GGevcWTJ25yAiKXAqliiDuLg49OrVC6dOnQIABAYGYvXq1XBzc5M4MtOj7j7Rtet/bdDU1MnM2rWqYsvmVsNNH2sQtfn+WPqoJREZH0fsiAAIIRAWFgZfX1+cOnUKrq6uCAsLQ3h4OJO6PGjTfUK9nq9HD9VnU09k9NktQ5vvDxGRPnFXLFm9f//9F59//jm2bNkCAGjRogU2b96MSpUqSRyZ+bCUzgqRkYbplmEp3x8ikgbLnRBp6fjx4+jVqxfu3r0LuVyOadOmYeLEibC15SoFa8O6c0RkqnTJdzgVS1bpzZs3mDx5Mtq0aYO7d++icuXKOHHiBKZMmcKkzkodP553sWUhgPh41XlERKaKv8HI6ty8eRM9e/bE+fPnAQB9+/bFypUrUbRoUYkjIymx7hwRWQImdmQ1hBAIDQ1FcHAwXrx4gWLFimHNmjX47LPPCnVda1k/Zemvk3XniMgSMLEjq/DkyRMMHjwYO3bsAAC0bt0amzZtQoUKFQp13Zz6rnp6qspcWNKOR2t4neq6c/fvZ988AVhHtwwiMn+SrrGbPn06ZDJZpo8yZcpIGRJZoMOHD8PHxwc7duyAra0t5s2bh0OHDuklqevaNfu6rPv3VccLUh7DFFnL61TXnQOyFxVm3TkiMheSb56oU6cOEhISNB9XrlyROiSyEGlpafjiiy/Qvn173L9/H9WqVcPp06fx5ZdfQl7I384KhWoEK6eRHfWxkBDVeebMWl6nGuvOEZG5k3wq1tbWlqN0pHf/+9//EBgYiEuXLgEABg0ahGXLlsHZ2Vmr5+e3nkyXHZRt2hTihUjMWl5nRgEB5tktg4gIMIHE7ubNmyhXrhwcHBzQpEkTzJ07F5UrV87x3NTUVKSmpmq+TklJMVaYZCaEEFizZg3GjBmDV69eoUSJEvj+++/x8ccfa30NbdaTWcsOSmt5nVmpu2UQEZkbSadimzRpgk2bNuH333/HunXr8PDhQzRv3hyPHz/O8fx58+bBzc1N8+Hl5WXkiMmUPXr0CB999BGGDRuGV69eoX379rhy5YrOSZ0268msZQeltbxOIiJLYVKdJ168eIEqVargiy++wJgxY7I9ntOInZeXFztPEPbv34+goCA8fPgQ9vb2mDdvHkJCQmBjo/3/XXTpPACozs1vB6U+uhRIWWZE/T0xxuskIqKcmW3nCWdnZ9SrVw83b97M8XEHBwe4urpm+iDr9vr1a4wePRqdOnXCw4cPUatWLZw9exZjxozRKakDdFtPZqwdlPpsSF8Q3ClKRGReTCqxS01NxbVr11CW8zqkhT/++AONGzfG8uXLAQDDhw/HhQsX4OfnV6Dr6bqezNA7KE2lzAh3ihIRmQ9Jp2LHjRuHLl26oEKFCkhMTMTs2bNx9OhRXLlyBRUrVsz3+boMTZLlEELg66+/xvjx45GamgoPDw/88MMP+OCDDwp13ago1YhYfo4cybyw3hBTpabYkN7SO08QEZkqXfIdSXfF3rt3Dz169EBSUhI8PDzQtGlTnDlzRqukjqzTP//8g379+mHfvn0AgPfeew+hoaEoXbp0oa9d0M4DhthBaYplRrhTlIjI9Ema2P34449S3p7MzK+//op+/frh0aNHcHBwwOLFizF8+HDIsi7+KiD1erKuXVVJXMbkztjryay1zAgRERWOSa2xI8rJq1evMGLECHzwwQd49OgR6tWrhwsXLmDEiBF6S+rUTGU9GcuMEBFRQZhUuRNdcY2d5bt8+TICAwNx7do1AMDo0aMxd+5cFClSxKD3lXo9GcuMEBGRmtmssSPKjVKpxPLlyzFx4kSkpaWhTJky2LBhAzp16qTV8wubmEm9nsyUpoWJiMh8cCqWTM6DBw/QqVMnjB07Fmlpafjwww8RGxurdVInde03fTGVaWEiIjIfnIolk7Jz504MHDgQT548gaOjI5YtW4bBgwdrvZZOXfst699q9dPNMSGSelqYiIikpUu+w8SOTMKLFy8wevRorFu3DgBQv359REREoGbNmlpfwxi135hkERGRsXGNHZmVCxcuoGfPnrhx4wZkMhnGjx+PWbNmwd7eXqfrFKb2mzYJW2QkMGpU5nt4eqrWwuljFJBJIxERFRbX2JFkFAoF5s+fj2bNmuHGjRsoX748Dh48iAULFuic1AEFr/2mzZo8Q7f3spR1gUREJC0mdiSJ+Ph4tGvXDhMnTkR6ejo++eQTxMTE4J133inwNQtS+02bhE2hUI3U5bRoQX0sJER1XkGYSk9YIiIyf0zsyOi2bdsGHx8fHD16FM7Ozli/fj1++uknuLu7F+q66pZgee2zkMuBR49Uf9Y2YYuK0n6KV1eGThqJiMi6cI0dGc2zZ88QHByMDRs2AADeeusthIeHo1q1anq5fsbab7lRKIBu3VTnliihXcIWFaXd/RMSdF8nZ4o9YYmIyHxxxI6M4syZM/Dz88OGDRsgk8nw1Vdf4eTJk3pL6tQCAoBt2/LfdBASoprq1KebN3VfJ8eesEREpE9M7Mig0tPTMWvWLLz99tv4+++/UaFCBURFRWH27Nmws7MzyD1Llsx76lI9Cqaeks1PmzZ5T/HKZIC7OzBtmu7r5NgTloiI9ImJHRnM7du30aZNG0ydOhUKhQLdu3dHTEwMWrVqZdD7aju65eGRf8Lm5aVK7Fas+O9Y1nPykt86ufzWBapjaNky7/sQEREBTOzIQMLDw+Hr64uTJ0+iaNGi2Lx5MyIiIlCsWDGD31vb0a3y5fNP2NT9WPNq7zV9OvD4ce73yWtzhXpdoDYxEBER5YeJHelVcnIyevbsiV69eiElJQXNmzdHTEwMevXqpXVbsMLSZRRMl36sAQHA7dvAkSNARITqc1wcoO0ywdxGEg3RE1ahUG362LJF9Zm7aomIrAN3xZLenDhxAr169cKdO3cgl8sxdepUTJo0Cba2xv1rlnF3rEyWuZRITqNgAQGAv792u1nl8uy7U/WxTk6XGPJj6A4ZRERkutgrlgrtzZs3mDlzJubOnQulUolKlSohPDwczZo1kzSunBIcLy9VUqfPBEfdo/b+/Zzr0emjR6221MWOs8ahTmgLOgJIhcN2cURUGLrkO0zsqFD++usv9OzZE+fOnQMA9OnTB6tWrTKZ98NYv1DVCRWQ8wihMRIqdYKZW108YyaY9B+OoBJRYemS73CNHRWIEAKhoaHw8/PDuXPn4Obmhi1btmDjxo0mk9QB/02d9uih+myohMYQ6+R0pUuxYzIOtosjImPjGjvS2ZMnTzB06FD89NNPAIBWrVph8+bNqFChgsSRSUuf6+QKgsWOTUt+7eJkMlUZHH9/jqASkf4wsSOdHDlyBH369MG9e/dga2uLmTNn4osvvoCcv5kA5Ly5wlhY7Ni0sF0cEUmBiR1pJS0tDVOnTsXChQshhEC1atUQHh6Ot956S+rQNKx9gbq6zEt+mzhY7Ng4OIJKRFLgGjvK1/Xr19GsWTMsWLAAQggMHDgQFy9eNKmkLjJS9z6tpqwgdehY7Ni0cASViKTAxI5yJYTA2rVrUb9+fVy8eBElSpTAjh07sG7dOri4uEgdnoalLVAvTJJqCps4SIXt4ohICix3QjlKSkrCwIED8fPPPwMA2rVrh40bN6J81oxBYpZW4kNfdeisfVraVJhCGRwiMn8sd0KFcuDAAfj4+ODnn3+GnZ0dFi9ejP3795tcUgdYVomP/HZRAqpdlNpOyxqjzAvljSOoRGRs3DxBGqmpqZg0aRKWLl0KAKhZsyYiIiJQv359iSPLnSUtUOcuSsskdRkcIrIuTOwIAPDnn38iMDAQsbGxAIBhw4Zh8eLFcHJykjiyvFnSAnVLSlIpMynL4BCRdeFUrJUTQuCbb75Bo0aNEBsbi5IlS2L37t1YvXq1ySd1gGUtULekJJWIiKTBxM6K/fPPP/jggw8wYsQIvH79Gp06dcKVK1fQpUsXqUPTmiWV+LCkJJWIiKTBxM5K7d27Fz4+Pti7dy8cHBywYsUK7N27F2XKlJE6NJ0VdoF6QWrGGYIlJalERCQNljuxMq9evcIXX3yBr7/+GgBQt25dREREoF69ehJHVngFKfERGanaiZpx04KnpyrBkmrHYk4xeXmpkjruoiQisj665DtM7KxITEwMAgMDcfXqVQBAcHAwFixYgCJFikgcmTT0VTPOEFiHjoiI1JjYUSZKpRIrVqzAl19+ibS0NJQuXRqhoaF47733pA5NMpZW2JiIiCwXCxSTxoMHD/Duu+9izJgxSEtLwwcffIDY2FirTuoAyypsTEREpMbEzoLt2rULPj4+OHDgAIoUKYLVq1dj9+7dKFWqlNShSY4144iIyBKxQLEFevHiBcaMGYO1a9cCAPz8/BAREYFatWpJHJnpYM04IiKyRByxszDR0dFo0KCBJqkbN24czpw5w6QuC9aMIyIiS8TEzkIoFAosWLAATZs2xY0bN1CuXDkcOHAAixYtgoODg9ThmRzWjCMiIkvExM4CxMfHo3379vjyyy+Rnp6Ojz/+GLGxsWjfvr3UoZm0whY2JiIiMjVcY2fmtm/fjsGDB+Pp06dwcnLCypUr0b9/f8hym2OkTAICAH9/1owjIiLLwMTOTD179gyjRo1CaGgoAKBRo0YIDw9H9erVJY7M/MjlQJs2UkdBRERUeJyKNUNnz55F/fr1ERoaCplMhkmTJuHUqVNM6oiIiKwcR+zMiEKhwLx58zB9+nQoFAp4eXlh8+bNaN26tdShERERkQlgYmcm7ty5g169euHEiRMAgG7duuHbb79F8eLFJY6MiIiITAWnYs3Ali1b4OPjgxMnTqBo0aLYtGkTtmzZwqSOiIiIMuGInQlLTk7GiBEjEBYWBgBo1qwZwsLCULlyZYkjIyIiIlPEETsTdfLkSfj5+SEsLAw2NjaYNm0ajh07xqSOiIiIcsUROxOTnp6OWbNmYfbs2VAqlfD29kZYWBhatGghdWhERERk4pjYmZBbt26hV69eOHPmDACgd+/eWLVqFdzc3CSOjIiIiMwBp2JNgBACGzduhJ+fH86cOQM3NzdERERg06ZNTOqIiIhIaxyxk9jTp08xdOhQbNu2DQDQsmVLbN68GRUrVpQ4MiIiIjI3HLGTUFRUFHx8fLBt2zbI5XLMnj0bR44cYVJHREREBcIROwmkpaVh2rRpWLBgAYQQqFKlCiIiItC4cWOpQyMiIiIzxsTOyK5fv46ePXsiOjoaANC/f3+sWLECLi4uEkdGRERE5o5TsUYihMC6devQoEEDREdHo3jx4vjpp5+wfv16JnVERESkFxyxM4LHjx9j0KBB2LlzJwCgbdu22LRpEzw9PSWOjIiIiCwJR+wM7ODBg/Dx8cHOnTthZ2eHhQsX4uDBg0zqiIiISO84Ymcgqamp+Oqrr7BkyRIAQI0aNRAREYEGDRpIHBkRERFZKiZ2BnD16lUEBgYiJiYGADB06FAsWbIETk5OEkdGREREloxTsXokhMDq1avRsGFDxMTEoGTJkvj555/x7bffMqkjIiIig+OInZ4kJiaif//++PXXXwEAHTt2xIYNG1C2bFmJIyMiIiJrwRE7Pdi3bx/q1auHX3/9Ffb29li2bBn27dvHpI6IiIiMiiN2hfDq1StMmDABq1atAgDUqVMHERER8PHxkTgyIiIiskZM7AroypUrCAwMxB9//AEAGDlyJBYsWABHR0eJI6OcKBTA8eNAQgJQtizQsiUgl0sdFRERkX5xKlZHSqUSK1aswFtvvYU//vgDpUqVwq+//oqVK1cyqTNRkZGAtzfQti0QGKj67O2tOk5ERGRJmNjpICEhAZ07d0ZISAhSU1Px/vvv48qVK+jcubPUoVEuIiOBrl2Be/cyH79/X3WcyR0REVkSJnZa2r17N3x8fPD777+jSJEi+Oabb7Bnzx6UKlVK6tAoFwoFMGoUIET2x9THQkJU5xEREVkCJnb5ePnyJYYNGwZ/f38kJSXB19cX0dHR+PzzzyGTyaQOj/Jw/Hj2kbqMhADi41XnERERWQImdvlITU3V1KYbO3Yszp49i9q1a0scFWkjIUG/5xEREZk67orNR/HixREREYFXr16hQ4cOUodDOtC2jCDLDRIRkaVgYqeFt99+W+oQqABatgQ8PVUbJXJaZyeTqR5v2dL4sRERERkCp2LJYsnlwIoVqj9nXQ6p/nr5ctazIyIiy8HEjixaQACwfTtQvnzm456equMBAdLERUREZAiciiWLFxAA+Puz8wQREVk+kxmxmzdvHmQyGUJCQqQOhSyQXA60aQP06KH6zKSOiIgskUkkdufPn8fatWvh4+MjdShEREREZkvyxO758+fo2bMn1q1bh+LFi0sdDhEREZHZkjyxGz58ON5//320b99e6lCIiIiIzJqkmyd+/PFHXLx4EefPn9fq/NTUVKSmpmq+TklJMVRoRERERGZHshG7+Ph4jBo1CmFhYShSpIhWz5k3bx7c3Nw0H15eXgaOkoiIiMh8yITIqSa/4e3atQsff/wx5Bm2JyoUCshkMtjY2CA1NTXTY0DOI3ZeXl5ITk6Gq6ur0WInIiIiMpaUlBS4ublple9INhXbrl07XLlyJdOxfv36oWbNmpgwYUK2pA4AHBwc4ODgYKwQiYiIiMyKZIld0aJFUbdu3UzHnJ2d4e7unu04EREREeVP8l2xRERERKQfJtVSLCoqSuoQiIiIiMwWR+yIiIiILIRJjdjpSr2hl/XsiIiIyFKp8xxtCpmYdWL37NkzAGA9OyIiIrJ4z549g5ubW57nSFbHTh+USiUePHiAokWLQiaT5XmuuuZdfHw8a95ZOL7X1oPvtfXge209+F5nJ4TAs2fPUK5cOdjY5L2KzqxH7GxsbODp6anTc1xdXfkXxUrwvbYefK+tB99r68H3OrP8RurUuHmCiIiIyEIwsSMiIiKyEFaT2Dk4OGDatGlsSWYF+F5bD77X1oPvtfXge104Zr15goiIiIj+YzUjdkRERESWjokdERERkYVgYkdERERkIawusZs3bx5kMhlCQkKkDoX0bPr06ZDJZJk+ypQpI3VYZCD3799Hr1694O7uDicnJ/j5+SE6OlrqsEjPvL29s/27lslkGD58uNShkZ6lp6dj8uTJqFSpEhwdHVG5cmXMnDkTSqVS6tDMilkXKNbV+fPnsXbtWvj4+EgdChlInTp1cPDgQc3XcrlcwmjIUJ4+fYoWLVqgbdu22LdvH0qVKoVbt26hWLFiUodGenb+/HkoFArN13/88Qc6dOiATz/9VMKoyBAWLFiA7777Dhs3bkSdOnVw4cIF9OvXD25ubhg1apTU4ZkNq0nsnj9/jp49e2LdunWYPXu21OGQgdja2nKUzgosWLAAXl5eCA0N1Rzz9vaWLiAyGA8Pj0xfz58/H1WqVEHr1q0liogM5fTp0/D398f7778PQPVvesuWLbhw4YLEkZkXq5mKHT58ON5//320b99e6lDIgG7evIly5cqhUqVK6N69O/7++2+pQyID2L17Nxo1aoRPP/0UpUqVQv369bFu3TqpwyIDS0tLQ1hYGPr3759vf3AyP2+//TYOHTqEGzduAABiYmJw4sQJdO7cWeLIzItVjNj9+OOPuHjxIs6fPy91KGRATZo0waZNm1C9enX8888/mD17Npo3b44///wT7u7uUodHevT333/j22+/xZgxYzBp0iScO3cOwcHBcHBwQJ8+faQOjwxk165d+Pfff9G3b1+pQyEDmDBhApKTk1GzZk3I5XIoFArMmTMHPXr0kDo0s2LxiV18fDxGjRqF/fv3o0iRIlKHQwb03nvvaf5cr149NGvWDFWqVMHGjRsxZswYCSMjfVMqlWjUqBHmzp0LAKhfvz7+/PNPfPvtt0zsLNj69evx3nvvoVy5clKHQgawdetWhIWFISIiAnXq1MHly5cREhKCcuXKISgoSOrwzIbFJ3bR0dFITExEw4YNNccUCgWOHTuGr7/+GqmpqVxgb6GcnZ1Rr1493Lx5U+pQSM/Kli2L2rVrZzpWq1Yt7NixQ6KIyNDu3LmDgwcPIjIyUupQyEDGjx+PL7/8Et27dweg+g/6nTt3MG/ePCZ2OrD4xK5du3a4cuVKpmP9+vVDzZo1MWHCBCZ1Fiw1NRXXrl1Dy5YtpQ6F9KxFixa4fv16pmM3btxAxYoVJYqIDC00NBSlSpXSLKwny/Py5UvY2GRe+i+Xy1nuREcWn9gVLVoUdevWzXTM2dkZ7u7u2Y6TeRs3bhy6dOmCChUqIDExEbNnz0ZKSgr/p2eBRo8ejebNm2Pu3Ln47LPPcO7cOaxduxZr166VOjQyAKVSidDQUAQFBcHW1uJ/bVmtLl26YM6cOahQoQLq1KmDS5cuYenSpejfv7/UoZkV/gshi3Hv3j306NEDSUlJ8PDwQNOmTXHmzBmO4ligt956Czt37sTEiRMxc+ZMVKpUCcuXL0fPnj2lDo0M4ODBg7h79y5/wVu4VatWYcqUKfj888+RmJiIcuXKYciQIZg6darUoZkVmRBCSB0EERERERWe1dSxIyIiIrJ0TOyIiIiILAQTOyIiIiILwcSOiIiIyEIwsSMiIiKyEEzsiIiIiCwEEzsiIiIiC8HEjoiIiMhCMLEjIovn7e2N5cuXa76WyWTYtWuXZPHo4vbt25DJZLh8+bLUoRCRGWBiR0RWJyEhAe+9955W506fPh1+fn6GDcgI2rRpg5CQEKnDICIDY2JHRGYhLS1Nb9cqU6YMHBwc9HY9bbx588ao9yMi68TEjoiMrk2bNhgxYgRGjBiBYsWKwd3dHZMnT0bG1tXe3t6YPXs2+vbtCzc3NwwaNAgAcOrUKbRq1QqOjo7w8vJCcHAwXrx4oXleYmIiunTpAkdHR1SqVAnh4eHZ7p91KvbevXvo3r07SpQoAWdnZzRq1Ahnz57Fhg0bMGPGDMTExEAmk0Emk2HDhg0AgLt378Lf3x8uLi5wdXXFZ599hn/++UdzTfVI3w8//IDKlSvDwcEBWVtzv3jxAq6urti+fXum43v27IGzszOePXumOfb333+jbdu2cHJygq+vL06fPq157PHjx+jRowc8PT3h5OSEevXqYcuWLZrH+/bti6NHj2LFihWa13H79m0t3ikiMjdM7IhIEhs3boStrS3Onj2LlStXYtmyZfj+++8znbNo0SLUrVsX0dHRmDJlCq5cuYJOnTohICAAsbGx2Lp1K06cOIERI0ZontO3b1/cvn0bhw8fxvbt27F69WokJibmGsfz58/RunVrPHjwALt370ZMTAy++OILKJVKdOvWDWPHjkWdOnWQkJCAhIQEdOvWDUIIfPTRR3jy5AmOHj2KAwcO4NatW+jWrVuma//111/Ytm0bduzYkeMaOWdnZ3Tv3h2hoaGZjoeGhqJr164oWrSo5thXX32FcePG4fLly6hevTp69OiB9PR0AMDr16/RsGFD/PLLL/jjjz8wePBg9O7dG2fPngUArFixAs2aNcOgQYM0r8PLy0u7N4qIzIsgIjKy1q1bi1q1agmlUqk5NmHCBFGrVi3N1xUrVhQfffRRpuf17t1bDB48ONOx48ePCxsbG/Hq1Stx/fp1AUCcOXNG8/i1a9cEALFs2TLNMQBi586dQggh1qxZI4oWLSoeP36cY6zTpk0Tvr6+mY7t379fyOVycffuXc2xP//8UwAQ586d0zzPzs5OJCYm5vm9OHv2rJDL5eL+/ftCCCEePXok7OzsRFRUlBBCiLi4OAFAfP/999nude3atVyv27lzZzF27FjN161btxajRo3KMxYiMn8csSMiSTRt2hQymUzzdbNmzXDz5k0oFArNsUaNGmV6TnR0NDZs2AAXFxfNR6dOnaBUKhEXF4dr167B1tY20/Nq1qyJYsWK5RrH5cuXUb9+fZQoUULr2K9duwYvL69Mo161a9dGsWLFcO3aNc2xihUrwsPDI89rNW7cGHXq1MGmTZsAAJs3b0aFChXQqlWrTOf5+Pho/ly2bFkA0IxEKhQKzJkzBz4+PnB3d4eLiwv279+Pu3fvav2aiMgyMLEjIpPl7Oyc6WulUokhQ4bg8uXLmo+YmBjcvHkTVapU0axhy5gw5sfR0VHnuIQQOd4j6/Gs8edm4MCBmunY0NBQ9OvXL9v17ezsNH9WP6ZUKgEAS5YswbJly/DFF1/g8OHDuHz5Mjp16qTXDSdEZB6Y2BGRJM6cOZPt62rVqkEul+f6nAYNGuDPP/9E1apVs33Y29ujVq1aSE9Px4ULFzTPuX79Ov79999cr+nj44PLly/jyZMnOT5ub2+faRQRUI3O3b17F/Hx8ZpjV69eRXJyMmrVqpXXy85Rr169cPfuXaxcuRJ//vkngoKCdHr+8ePH4e/vj169esHX1xeVK1fGzZs3830dRGR5mNgRkSTi4+MxZswYXL9+HVu2bMGqVaswatSoPJ8zYcIEnD59GsOHD8fly5dx8+ZN7N69GyNHjgQA1KhRA++++y4GDRqEs2fPIjo6GgMHDsxzVK5Hjx4oU6YMPvroI5w8eRJ///03duzYodl16u3tjbi4OFy+fBlJSUlITU1F+/bt4ePjg549e+LixYs4d+4c+vTpg9atW2ebPtZG8eLFERAQgPHjx6Njx47w9PTU6flVq1bFgQMHcOrUKVy7dg1DhgzBw4cPM53j7e2Ns2fP4vbt20hKStKM9hGRZWFiR0SS6NOnD169eoXGjRtj+PDhGDlyJAYPHpznc3x8fHD06FHcvHkTLVu2RP369TFlyhTNmjNANZXp5eWF1q1bIyAgAIMHD0apUqVyvaa9vT3279+PUqVKoXPnzqhXrx7mz5+vGTn85JNP8O6776Jt27bw8PDAli1bNOVSihcvjlatWqF9+/aoXLkytm7dWuDvx4ABA5CWlob+/fvr/NwpU6agQYMG6NSpE9q0aaNJVDMaN24c5HI5ateuDQ8PD66/I7JQMiGyFFYiIjKwNm3awM/PL1ObL2sXHh6OUaNG4cGDB7C3t5c6HCIyU7ZSB0BEZM1evnyJuLg4zJs3D0OGDGFSR0SFwqlYIiIJLVy4EH5+fihdujQmTpwodThEZOY4FUtERERkIThiR0RERGQhmNgRERERWQgmdkREREQWgokdERERkYVgYkdERERkIZjYEREREVkIJnZEREREFoKJHREREZGFYGJHREREZCH+DwXlOWPcq0DbAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# model prediction\n", + "yhat = logGeom[te].dot(alpha[1:]) + alpha[0]\n", + "\n", + "M1, M2 = max(y[te]), min(y[te])\n", + "plt.plot(yhat, y[te], \"bo\", label=\"sample of the testing set\")\n", + "plt.plot([M1, M2], [M1, M2], \"k-\", label=\"identity\")\n", + "plt.xlabel(\"predictor yhat\"), plt.ylabel(\"real y\"), plt.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stability selection" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + " \n", + "FORMULATION: R1\n", + " \n", + "MODEL SELECTION COMPUTED: \n", + " Stability selection\n", + " \n", + "STABILITY SELECTION PARAMETERS: \n", + " numerical_method : Path-Alg\n", + " method : first\n", + " B = 50\n", + " q = 10\n", + " percent_nS = 0.5\n", + " threshold = 0.7\n", + " lamin = 0.01\n", + " Nlam = 50\n", + " " + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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27fvc8dERERERES5jEaJw5QpU1I0hjYxMcGUKVM+d0xERERERJTLaJQ4pKZSqTSqvvTmzRt07twZX331FapXr442bdrg3r17nxICERERERFlA40SByMjI5w/f16Z9vLygqGhoUYFDhkyBP7+/rhy5Qrat2+PIUOGaLQ9ERERERFlP40HgOvcuTMqV64MEYGfnx927dqV6e319PTg4OCgTNerVw9LlizRJAQiIiIiIsoBGiUOdnZ2uHnzJs6dOwcAnzwA3C+//IIOHTp89PZERERERJQ9NB4ALn/+/GjSpImy7GMHgJszZw4CAgKwcuXKNMsWLVqERYsWKdPR0dEa75+IiIiIiD6fTLVxsLOzAwCo1WoYGhoq/5KnNbVw4ULs3LkTBw8eTDfpcHFxQUhIiPJPrVZrXAYREREREX0+mXrjsGHDBgCfZwC4RYsWYevWrTh27NgnVXMiIiIiIqLsk6k3Dn369AEANGjQ4JMKCwkJwdixYxEVFYWmTZuievXqsLW1/aR9EhERERFR1svUG4c3b97A3d0dYWFhOHDgQJrl7/aU9CEWFhYQEc0iJCIiIiKiHJepxGHevHlYuXIlIiIi8NNPP6VYplKpMp04EBERERHRlylTiUO5cuVw4MABfP/991i6dGlWx0RERERERLlMpto49O7dGwDg7e2dpcEQEREREVHupFEbh0ePHn1SGwciIiIiIvoyadTGITw8nG0ciIiIiIjyoEwlDh07dkTHjh3ZxoGIiIiIKI/KVBuHZEuXLkVERATOnDkDAEhISEBcXFyWBEZERERERLmHRonDrl27ULduXaWxtK+vLzp37pwVcRERERERUS6iUeIwe/ZseHt7w9TUFABgY2OD+/fvZ0lgRERERESUe2iUOGhpaaFQoUIp5unq6n7WgIiIiIiIKPfRKHEwNDREeHg4VCoVAODEiRPK2wciIiIiIvrvylSvSsnmz58PBwcHBAUFoUmTJggICMDevXuzKjYiIiIiIsolNEocateujX/++Qeenp4QEdjb28PExCSLQiMiIiIiotxCo8QBAIyNjVGzZk2oVComDUREREREeYRGbRxu3bqFqlWromLFirCyskK1atXg5+eXVbEREREREVEuoVHiMGzYMPzwww949uwZnj17hkmTJmHo0KFZFRsREREREeUSGiUOz549g7OzszLds2dPREVFfe6YiIiIiIgol9GojYO2tjZu3ryJSpUqAQD8/f2hpaVR7kFERHlQGdf92VbWvXntsq0sIqK8RKPEYfbs2WjcuDFq1KgBlUqFK1euYOPGjVkVGxERERER5RKZShxevHiBp0+fok2bNrh58ybOnz8PEYGZmRkqVqyY1TESEREREVEOy1TiMGHCBLRs2RJlypRBkSJF0L59ewDAxo0bsW7dOqxYsSJLgyR6H1Z/ICIiIsoemWqgcPr0aTg6OqaZ37t3b5w+ffqzB0VERERERLlLphIHbW3t9y5TqVQaFRgQEAB7e3t89dVXqFu3Lm7evKnR9kRERERElP0ylTgkJCTgxYsXaeY/f/4c8fHxGhX47bffYsiQIbh9+zYmTJiAgQMHarQ9ERERERFlv0wlDl9//TV69+6NZ8+eKfOePXuG/v37o2fPnpkuLCIiAj4+PujVqxcAwNHREUFBQbh3755mURMRERERUbbKVOIwefJkmJiYoGTJkqhRowZq1KiBkiVLwtDQEFOmTMl0YQ8ePEDx4sWRL9/bNtkqlQqlSpVCcHDwx0VPRERERETZIlO9Kmlra2P9+vWYOnUqfHx8AAA1a9ZEuXLlNC4wdZsIEUmzzqJFi7Bo0SJlOjo6WuNysgJ71cl9+JsQZSw39D7Gc5WIvkS54fqZm2g0AFy5cuU+KllIVrJkSYSEhCAhIQH58uWDiODBgwcoVapUivVcXFzg4uKiTFtYWHx0mURERERE9OkyVVXpcylatChq1KiBTZs2AQDc3d1RpkwZlClTJjvDICIiIiIiDWn0xuFzWLVqFfr164c5c+bAyMgI69evz+4QiIjylC/h9TcREeV+2Z44WFlZ4dy5c9ldLBERERERfYJsrapERERERERfJiYORERERESUISYORERERESUISYORERERESUISYORERERESUISYORERERESUoWzvjpWIiIiI6EvAcXBS4hsHIiIiIiLKEBMHIiIiIiLKEBMHIiIiIiLKEBMHIiIiIiLKkEpEJKeDyEj+/PlRpEiRnA7jo0RHR0OtVud0GPQO/iZEGcst50luiYOISBNf8rXr8ePHiI2NTXfZF5E4fMksLCwQEhKS02HQO/ibEGUst5wnuSUOIiJN/FevXayqREREREREGWLiQEREREREGWLikMVcXFxyOgRKhb8JUcZyy3mSW+IgItLEf/Xa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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " STABILITY SELECTION : \n", + " Selected variables : intercept p__Bacteroidetes o__Acidobacteriales c__Acidobacteria-6 k__Bacteria \n", + " Running time : 44.955s\n", + "\n", + "['Life::k__Bacteria::p__Bacteroidetes'\n", + " 'Life::k__Bacteria::p__Acidobacteria::c__Acidobacteriia::o__Acidobacteriales'\n", + " 'Life::k__Bacteria::p__Acidobacteria::c__Acidobacteria-6'\n", + " 'Life::k__Bacteria']\n" + ] + } + ], + "source": [ + "problem = classo_problem(logGeom[tr], y[tr], label=label_short)\n", + "\n", + "problem.formulation.w = 1 / nleaves\n", + "problem.formulation.intercept = True\n", + "problem.formulation.concomitant = False\n", + "\n", + "\n", + "problem.model_selection.PATH = False\n", + "problem.model_selection.CV = False\n", + "# can change q, B, nS, method, threshold etc in problem.model_selection.StabSelparameters\n", + "\n", + "problem.solve()\n", + "\n", + "print(problem, problem.solution)\n", + "\n", + "selection = problem.solution.StabSel.selected_param[1:] # exclude the intercept\n", + "print(label[selection])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prediction plot" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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1CkcP34c3bp1Q5MmTbJMH+ujUKFCCAkJwYgRI/D+++/Dx8fHoMdXqFABO3fuxOHDh3Hx4kX069cPd+/ezXCOn58fjh07huvXr+PBgwea0T4isi1M7IhIEt26dcPLly9Rp04dDBw4EIMHD0bfvn2zfUxAQAD27duHK1euoFGjRqhRowbGjh2rWXMGqKYyfX190aRJE4SEhKBv374oWrSozms6Oztjx44dKFq0KFq3bg1/f39MmzZNM3L4ySef4IMPPkCzZs3g7e2N1atXa8qlFCpUCI0bN0bz5s1Rrlw5rFmzJtdfj88//xypqano1auXwY8dO3YsatasiZYtW6Jp06aaRDW9r776CnK5HNWqVYO3tzfX3xHZKJkQmQorERGZWNOmTREUFJShzZe9i4qKwtChQ3H79m04OztLHQ4RWSlHqQMgIrJnL168wLVr1zB16lT069ePSR0R5QmnYomIJDRjxgwEBQWhWLFiGDVqlNThEJGV41QsERERkY3giB0RERGRjWBiR0RERGQjmNgRERER2QgmdkREREQ2gokdERERkY1gYkdERERkI5jYEREREdkIJnZERERENoKJHREREZGN+B+bUV3rLiseAAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "te = np.array([i for i in range(len(y)) if i not in tr])\n", + "alpha = problem.solution.StabSel.refit\n", + "yhat = logGeom[te].dot(alpha[1:]) + alpha[0]\n", + "\n", + "M1, M2 = max(y[te]), min(y[te])\n", + "plt.plot(yhat, y[te], \"bo\", label=\"sample of the testing set\")\n", + "plt.plot([M1, M2], [M1, M2], \"k-\", label=\"identity\")\n", + "plt.xlabel(\"predictor yhat\"), plt.ylabel(\"real y\"), plt.legend()\n", + "plt.tight_layout()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ritme_wclasso", + "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.8.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 1d1c7983c6bbdb6c32b9bbe27540db6411472018 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Thu, 2 May 2024 14:22:08 +0200 Subject: [PATCH 02/28] implement calculation of matrix A --- ci/recipe/meta.yaml | 4 + experiments/implement_matrixA.ipynb | 314 ++++++++++++++++++++++++++++ experiments/test_classo.ipynb | 300 ++++---------------------- 3 files changed, 361 insertions(+), 257 deletions(-) create mode 100644 experiments/implement_matrixA.ipynb diff --git a/ci/recipe/meta.yaml b/ci/recipe/meta.yaml index 9b95255..6ae7e65 100644 --- a/ci/recipe/meta.yaml +++ b/ci/recipe/meta.yaml @@ -22,6 +22,10 @@ requirements: - importlib-metadata - qiime2 {{ qiime2_epoch }}.* - q2-feature-table {{ qiime2_epoch }}.* + - q2-feature-classifier {{ qiime2_epoch }}.* + - q2-phylogeny {{ qiime2_epoch }}.* + # todo: check if q2-types is really needed - if not remove + - q2-types {{ qiime2_epoch }}.* - lightning # todo: once newest version is passing all tests: upgrade mlflow - mlflow==2.11.3 diff --git a/experiments/implement_matrixA.ipynb b/experiments/implement_matrixA.ipynb new file mode 100644 index 0000000..8aebbd6 --- /dev/null +++ b/experiments/implement_matrixA.ipynb @@ -0,0 +1,314 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from skbio import TreeNode\n", + "import qiime2 as q2\n", + "import pandas as pd\n", + "import skbio\n", + "from qiime2.plugins import phylogeny" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "def create_matrix_from_tree(tree):\n", + " # Get all leaves and create a mapping from leaf names to indices\n", + " leaves = list(tree.tips())\n", + " leaf_names = [leaf.name for leaf in leaves]\n", + " # map each leaf name to unique index\n", + " leaf_index_map = {name: idx for idx, name in enumerate(leaf_names)}\n", + "\n", + " # Get the number of leaves and internal nodes\n", + " num_leaves = len(leaf_names)\n", + " # root is not included\n", + " internal_nodes = list(tree.non_tips())\n", + "\n", + " # Create the identity matrix for the leaves: A1 (num_leaves x num_leaves)\n", + " A1 = np.eye(num_leaves)\n", + "\n", + " # Create the matrix for the internal nodes: A2 (num_leaves x\n", + " # num_internal_nodes)\n", + " # initialise it with zeros\n", + " A2 = np.zeros((num_leaves, len(internal_nodes)))\n", + "\n", + " # Populate A2 with 1s for the leaves linked by each internal node\n", + " # iterate over all internal nodes to find descendents of this node and mark\n", + " # them accordingly\n", + " for j, node in enumerate(internal_nodes):\n", + " descendant_leaves = {leaf.name for leaf in node.tips()}\n", + " for leaf_name in leaf_names:\n", + " if leaf_name in descendant_leaves:\n", + " A2[leaf_index_map[leaf_name], j] = 1\n", + "\n", + " # Concatenate A1 and A2 to create the final matrix A\n", + " A = np.hstack((A1, A2))\n", + "\n", + " return A" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example data" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " /-f1\n", + " /n1------|\n", + "-n2------| \\-f2\n", + " |\n", + " \\-f3\n" + ] + } + ], + "source": [ + "# Create the tree nodes with lengths\n", + "n1 = TreeNode(name=\"n1\")\n", + "f1 = TreeNode(name=\"f1\", length=1.0)\n", + "f2 = TreeNode(name=\"f2\", length=1.0)\n", + "n2 = TreeNode(name=\"n2\")\n", + "f3 = TreeNode(name=\"f3\", length=1.0)\n", + "\n", + "# Build the tree structure with lengths\n", + "n1.extend([f1, f2])\n", + "n2.extend([n1, f3])\n", + "n1.length = 1.0\n", + "n2.length = 1.0\n", + "\n", + "# n2 is the root of this tree\n", + "tree = n2\n", + "print(tree.ascii_art())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 0., 0., 1.],\n", + " [0., 1., 0., 1.],\n", + " [0., 0., 1., 0.]])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A = create_matrix_from_tree(tree)\n", + "A" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Real data: MA2" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(9478, 5580)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# read feature table\n", + "art_feature_table = q2.Artifact.load(\"data/220728_monthly/all_otu_table_filt.qza\")\n", + "df_ft = art_feature_table.view(pd.DataFrame)\n", + "df_ft.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(5608, 2)\n", + "(5580, 2)\n" + ] + } + ], + "source": [ + "path_to_taxonomy = \"data/220728_monthly/otu_taxonomy_all.qza\"\n", + "art_taxonomy = q2.Artifact.load(path_to_taxonomy)\n", + "df_taxonomy = art_taxonomy.view(pd.DataFrame)\n", + "print(df_taxonomy.shape)\n", + "\n", + "# Filter the taxonomy based on the feature table\n", + "df_taxonomy_f = df_taxonomy[df_taxonomy.index.isin(df_ft.columns.tolist())]\n", + "print(df_taxonomy_f.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "870198" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# read silva phylo tree\n", + "path_to_phylo = \"data/220728_monthly/silva-138-99-rooted-tree.qza\"\n", + "art_phylo = q2.Artifact.load(path_to_phylo)\n", + "tree_phylo = art_phylo.view(skbio.TreeNode)\n", + "# total nodes\n", + "tree_phylo.count()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "11159" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# filter tree by feature table: this prunes a phylogenetic tree to match the\n", + "# input ids\n", + "(art_phylo_f,) = phylogeny.actions.filter_tree(tree=art_phylo, table=art_feature_table)\n", + "tree_phylo_f = art_phylo_f.view(skbio.TreeNode)\n", + "\n", + "# total nodes\n", + "tree_phylo_f.count()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# ensure that # leaves in tree == feature table dimension\n", + "num_leaves = tree_phylo_f.count(tips=True)\n", + "assert num_leaves == df_ft.shape[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Root is not included\n" + ] + }, + { + "data": { + "text/plain": [ + "array([[1., 0., 0., ..., 0., 0., 0.],\n", + " [0., 1., 0., ..., 0., 0., 0.],\n", + " [0., 0., 1., ..., 0., 0., 0.],\n", + " ...,\n", + " [0., 0., 0., ..., 0., 1., 1.],\n", + " [0., 0., 0., ..., 1., 1., 1.],\n", + " [0., 0., 0., ..., 1., 1., 1.]])" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_ma2 = create_matrix_from_tree(tree_phylo_f)\n", + "A_ma2" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# verififcation\n", + "# no all 1 in one column\n", + "assert not np.any(np.all(A_ma2 == 1.0, axis=0))\n", + "\n", + "# shape should be = feature_count + node_count\n", + "nb_features = df_ft.shape[1]\n", + "nb_non_leaf_nodes = len(list(tree_phylo_f.non_tips()))\n", + "\n", + "assert nb_features + nb_non_leaf_nodes == A_ma2.shape[1]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ritme_wclasso", + "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.8.19" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/experiments/test_classo.ipynb b/experiments/test_classo.ipynb index dd5f480..0f95f5b 100644 --- a/experiments/test_classo.ipynb +++ b/experiments/test_classo.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -59,7 +59,16 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "label" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -74,6 +83,15 @@ "# 3704 = 3379 OTUs + 325 nodes in tree" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "A" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -83,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -97,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -114,31 +132,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " \n", - " \n", - "FORMULATION: R1\n", - " \n", - "MODEL SELECTION COMPUTED: \n", - " Cross Validation\n", - " \n", - "CROSS VALIDATION PARAMETERS: \n", - " numerical_method : not specified\n", - " one-SE method : True\n", - " Nsubset = 5\n", - " lamin = 0.001\n", - " Nlam = 80\n", - " with log-scale\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "problem = classo_problem(logGeom[tr], y[tr], label=label_short)\n", "\n", @@ -173,42 +169,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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x7Fjx9CoUSNdE6nSyp09exYvvPACJkyYgMjISLi6uuLtt9+Gubk51Go10tLScO/ePdjY2CA1NRUJCQmwsrLCsGHD0LZtWwwbNgwRERHl/OsREdVcclMHQEREplGnTh1s3LgRAwcOhEajgb29PbZs2YK6deviq6++wosvvggnJyf07NkTZmZmsLKyKnE9np6euqQhMTFRr2bgwoULWLNmDY4dO6abFh4ejpiYGBw/flzX+5KhcqmpqejcuTMA4Pz585g4cSIAQKPRQC6XY/r06Xjrrbfg5+eHPXv2ICAgACqVCr/++iusra3RoEGDcv3diIhqOokQQpg6CCIiqloyMzNha2sLAFi7di1Wr16Nv/76Szd/zJgx+Omnn9CjRw+88847+PTTT2Fvbw+lUomlS5catY2AgADs378fzs7OBsuNHDkS8+fPh5OTE3r37o0dO3YgOTkZc+bMwfLly7Fy5UqcP38e9evXx7Zt23DgwAGMGjUK48ePh5mZGb788kusXLny6X8MIiLSwwSCiIiK+eSTT7B161ao1Wo4OjpixYoVaNSokanDIiKiKoAJBBERERERGY0PURMRERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdGYQBARERERkdH+H5KHPQ+d3iTvAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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imrVquHq1avo2bMnatasiQEDBhTbdvHixVp37K1SpQquXr2ql6xERERERC+Sp0309MzT1N69exenT5/WPM6cOaOXAd+3bt1C48aNcefOHZiYmEBEULt2bZw8ebJYD8bj/v3vf+P69etYvnz5U4/h5OTEAoOIiIiI6Bk87bt0mXowSmJra4uAgAAEBAQ86y5K5ODgAC8vL2zevBkjR45EcHAwnJ2dixUXqampqFWrFoyMjHD//n3s3r0bo0eP1msWIiIiqvicZ/6qdASi5ybps1eVjvBUZRrk/bwFBQUhKCgITZo0wWeffYZ169YBAMaMGYNffvkFABAcHIyWLVvCw8MD7du3R7du3fDWW28pGZuIiIiI6IX3zJdIVWa8RIqIiMhwsAeDXiQVoQfjad+lK2QPBhERERERVU7PXGBkZmbiwoUL+sxCRERERESVnE4FRvfu3ZGRkYGsrCx4eHigV69e+Oijj8orGxERERERVTI6FRg3b95E9erVsWfPHrz++uuIjY3FTz/9VE7RiIiIiIiostGpwMjLywMAHDlyBN26dYOpqWmxm+IREREREdGLS6cCo0WLFujevTt2796Nrl27Ijs7mwUGERERERFp6HSjvUWLFiE8PBweHh6wtLTEtWvXMG3atPLKRkRERERElYxOPRh9+vRBnz590KBBAwBAnTp1sGTJknIJRkRERERElU+ZejDy8/Px6NEjqNVq5OTkoPDefJmZmcjOzi7XgEREREREVHmUqQdj/vz5sLa2xvnz52FlZQVra2tYW1ujadOmGDp0aHlnJCIiIiKiSqJMBcbHH38MtVqNcePGQa1Wax4ZGRkIDAws74xERERERFRJ6DTIe+XKlVCr1bhx4wby8/M16+vVq6f3YEREREREVPnoVGB8++23eOedd2BiYgJjY2MAgEqlwq1bt8olHBERERERVS46FRhz587F6dOn4ebmVl55iIiIiIioEtNpmlp7e3sWF0REREREVCqdCoy+ffviq6++Qnp6OrKzszUPIiIiIiIiQMdLpGbOnAkAePfdd6FSqSAiUKlUKCgoKJdwRERERERUuehUYKjV6vLKQUREREREBkCnS6QA4NatWzh69CiA/93hm4iIiIiICNCxB2PHjh2YOnUqACApKQkXL17EBx98gD179pRLOEPmPPNXpSMQPTdJn72qdAQiIiJ6TnTq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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " CROSS VALIDATION : \n", - " Intercept : 5.9122282516799585\n", - " Selected variables : p__Bacteroidetes o__Acidobacteriales k__Bacteria \n", - " Running time : 12.524s\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "problem.solve()\n", "print(problem.solution)" @@ -216,19 +179,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['Life::k__Bacteria::p__Bacteroidetes'\n", - " 'Life::k__Bacteria::p__Acidobacteria::c__Acidobacteriia::o__Acidobacteriales'\n", - " 'Life::k__Bacteria']\n" - ] - } - ], + "outputs": [], "source": [ "# ! class solution_CV: defined in @solver.py L930\n", "selection = problem.solution.CV.selected_param[1:] # exclude the intercept\n", @@ -237,82 +190,18 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['__class__',\n", - " '__delattr__',\n", - " '__dict__',\n", - " '__dir__',\n", - " '__doc__',\n", - " '__eq__',\n", - " '__format__',\n", - " '__ge__',\n", - " '__getattribute__',\n", - " '__gt__',\n", - " '__hash__',\n", - " '__init__',\n", - " '__init_subclass__',\n", - " '__le__',\n", - " '__lt__',\n", - " '__module__',\n", - " '__ne__',\n", - " '__new__',\n", - " '__reduce__',\n", - " '__reduce_ex__',\n", - " '__repr__',\n", - " '__setattr__',\n", - " '__sizeof__',\n", - " '__str__',\n", - " '__subclasshook__',\n", - " '__weakref__',\n", - " 'beta',\n", - " 'formulation',\n", - " 'graphic',\n", - " 'index_1SE',\n", - " 'index_min',\n", - " 'label',\n", - " 'lambda_1SE',\n", - " 'lambda_min',\n", - " 'logscale',\n", - " 'refit',\n", - " 'save1',\n", - " 'save2',\n", - " 'selected_param',\n", - " 'standard_error',\n", - " 'time',\n", - " 'xGraph',\n", - " 'yGraph']" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "dir(problem.solution.CV)" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.06649435996665047" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# selected lambda with 1-standard-error method\n", "problem.solution.CV.lambda_1SE" @@ -320,20 +209,9 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.0023974349678010775" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# selected lambda without 1-standard-error method\n", "problem.solution.CV.lambda_min" @@ -348,20 +226,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "3705" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# define test set\n", "te = np.array([i for i in range(len(y)) if i not in tr])\n", @@ -378,20 +245,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB0KElEQVR4nO3dd1hT59sH8G8IQ4agIk5Q3Btw1FlXXa2tpaW2Kg7coyriqtW6956trVqLA7BaRauttk7cExVs9afWoqJiEbXgBEme94+8SdkkkORkfD/XxYWcnJxzhyjcPuO+ZUIIASIiIiIyezZSB0BERERE+sHEjoiIiMhCMLEjIiIishBM7IiIiIgsBBM7IiIiIgvBxI6IiIjIQjCxIyIiIrIQTOyIiIiILISt1AEUhlKpxIMHD1C0aFHIZDKpwyEiIiLSOyEEnj17hnLlysHGJu8xObNO7B48eAAvLy+pwyAiIiIyuPj4eHh6euZ5jlkndkWLFgWgeqGurq4SR0NERESkfykpKfDy8tLkPXkx68ROPf3q6urKxI6IiIgsmjbLzrh5goiIiMhCMLEjIiIishBM7IiIiIgshFmvsdOWQqHAmzdvpA6DyCrY2dlBLpdLHQYRkVWy6MROCIGHDx/i33//lToUIqtSrFgxlClThvUliYiMzKITO3VSV6pUKTg5OfGXDJGBCSHw8uVLJCYmAgDKli0rcURERNbFYhM7hUKhSerc3d2lDofIajg6OgIAEhMTUapUKU7LEhEZkcVunlCvqXNycpI4EiLro/53x7WtRETGZbGJnRqnX4mMj//uiIikYfGJHelP37598dFHHxn8Prt27ULVqlUhl8sREhKi9fOmT58OPz8/g8VlaFFRUZDJZNzsQ0REBcbEjkzOkCFD0LVrV8THx2PWrFk5niOTybBr1y6jxOPt7Y3ly5fr9Zpt2rTJlrQ2b94cCQkJcHNz0+u99C2n2MkwFAogKgrYskX1WaGQOiIiMnUWu3lCnxQK4PhxICEBKFsWaNkS4Hpww3j+/DkSExPRqVMnlCtXTupwjMre3h5lypSROgwyEZGRwKhRwL17/x3z9ARWrAACAqSLi4hMG0fs8hEZCXh7A23bAoGBqs/e3qrjhrJ9+3bUq1cPjo6OcHd3R/v27fHixQsAwPnz59GhQweULFkSbm5uaN26NS5evJjp+TKZDGvWrMEHH3wAJycn1KpVC6dPn8Zff/2FNm3awNnZGc2aNcOtW7c0z1FPY65ZswZeXl5wcnLCp59+mue0oBACCxcuROXKleHo6AhfX19s3749z9f29OlT9OnTB8WLF4eTkxPee+893Lx5E4BqKrJo0aIAgHfeeQcymQxRUVHZruHt7Q0A+PjjjyGTyTRfq23evBne3t5wc3ND9+7d8ezZswLH3KZNG9y5cwejR4+GTCbLtHbs1KlTaNWqFRwdHeHl5YXg4GDN+wQAq1evRrVq1VCkSBGULl0aXbt2BaCa0j569ChWrFihuebt27ezTcVu2LABxYoVw++//45atWrBxcUF7777LhISEjT3SE9PR3BwMIoVKwZ3d3dMmDABQUFBeU6Z37lzB126dEHx4sXh7OyMOnXqYO/evZrHr169is6dO8PFxQWlS5dG7969kZSUlGfspF+RkUDXrpmTOgC4f1913JA/f4jIzAkzlpycLACI5OTkbI+9evVKXL16Vbx69arA19+xQwiZTAgg84dMpvrYsaMw0efswYMHwtbWVixdulTExcWJ2NhY8c0334hnz54JIYQ4dOiQ2Lx5s7h69aq4evWqGDBggChdurRISUnRXAOAKF++vNi6dau4fv26+Oijj4S3t7d45513xG+//SauXr0qmjZtKt59913Nc6ZNmyacnZ3FO++8Iy5duiSOHj0qqlatKgIDAzXnBAUFCX9/f83XkyZNEjVr1hS//fabuHXrlggNDRUODg4iKioq19f34Ycfilq1aoljx46Jy5cvi06dOomqVauKtLQ0kZqaKq5fvy4AiB07doiEhASRmpqa7RqJiYkCgAgNDRUJCQkiMTFR8xpcXFxEQECAuHLlijh27JgoU6aMmDRpUoFjfvz4sfD09BQzZ84UCQkJIiEhQQghRGxsrHBxcRHLli0TN27cECdPnhT169cXffv2FUIIcf78eSGXy0VERIS4ffu2uHjxolixYoUQQoh///1XNGvWTAwaNEhzzfT0dHHkyBEBQDx9+lQIIURoaKiws7MT7du3F+fPnxfR0dGiVq1amd6T2bNnixIlSojIyEhx7do1MXToUOHq6prpfcrq/fffFx06dBCxsbHi1q1bYs+ePeLo0aNCCNXfv5IlS4qJEyeKa9euiYsXL4oOHTqItm3b5hl7Vvr492et0tOF8PTM/nMn488f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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# model prediction\n", "yhat = logGeom[te].dot(alpha[1:]) + alpha[0]\n", @@ -413,68 +269,9 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " \n", - " \n", - "FORMULATION: R1\n", - " \n", - "MODEL SELECTION COMPUTED: \n", - " Stability selection\n", - " \n", - "STABILITY SELECTION PARAMETERS: \n", - " numerical_method : Path-Alg\n", - " method : first\n", - " B = 50\n", - " q = 10\n", - " percent_nS = 0.5\n", - " threshold = 0.7\n", - " lamin = 0.01\n", - " Nlam = 50\n", - " " - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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27fvc8dERERERES5jEaJw5QpU1I0hjYxMcGUKVM+d0xERERERJTLaJQ4pKZSqTSqvvTmzRt07twZX331FapXr442bdrg3r17nxICERERERFlA40SByMjI5w/f16Z9vLygqGhoUYFDhkyBP7+/rhy5Qrat2+PIUOGaLQ9ERERERFlP40HgOvcuTMqV64MEYGfnx927dqV6e319PTg4OCgTNerVw9LlizRJAQiIiIiIsoBGiUOdnZ2uHnzJs6dOwcAnzwA3C+//IIOHTp89PZERERERJQ9NB4ALn/+/GjSpImy7GMHgJszZw4CAgKwcuXKNMsWLVqERYsWKdPR0dEa75+IiIiIiD6fTLVxsLOzAwCo1WoYGhoq/5KnNbVw4ULs3LkTBw8eTDfpcHFxQUhIiPJPrVZrXAYREREREX0+mXrjsGHDBgCfZwC4RYsWYevWrTh27NgnVXMiIiIiIqLsk6k3Dn369AEANGjQ4JMKCwkJwdixYxEVFYWmTZuievXqsLW1/aR9EhERERFR1svUG4c3b97A3d0dYWFhOHDgQJrl7/aU9CEWFhYQEc0iJCIiIiKiHJepxGHevHlYuXIlIiIi8NNPP6VYplKpMp04EBERERHRlylTiUO5cuVw4MABfP/991i6dGlWx0RERERERLlMpto49O7dGwDg7e2dpcEQEREREVHupFEbh0ePHn1SGwciIiIiIvoyadTGITw8nG0ciIiIiIjyoEwlDh07dkTHjh3ZxoGIiIiIKI/KVBuHZEuXLkVERATOnDkDAEhISEBcXFyWBEZERERERLmHRonDrl27ULduXaWxtK+vLzp37pwVcRERERERUS6iUeIwe/ZseHt7w9TUFABgY2OD+/fvZ0lgRERERESUe2iUOGhpaaFQoUIp5unq6n7WgIiIiIiIKPfRKHEwNDREeHg4VCoVAODEiRPK2wciIiIiIvrvylSvSsnmz58PBwcHBAUFoUmTJggICMDevXuzKjYiIiIiIsolNEocateujX/++Qeenp4QEdjb28PExCSLQiMiIiIiotxCo8QBAIyNjVGzZk2oVComDUREREREeYRGbRxu3bqFqlWromLFirCyskK1atXg5+eXVbEREREREVEuoVHiMGzYMPzwww949uwZnj17hkmTJmHo0KFZFRsREREREeUSGiUOz549g7OzszLds2dPREVFfe6YiIiIiIgol9GojYO2tjZu3ryJSpUqAQD8/f2hpaVR7kFERHlQGdf92VbWvXntsq0sIqK8RKPEYfbs2WjcuDFq1KgBlUqFK1euYOPGjVkVGxERERER5RKZShxevHiBp0+fok2bNrh58ybOnz8PEYGZmRkqVqyY1TESEREREVEOy1TiMGHCBLRs2RJlypRBkSJF0L59ewDAxo0bsW7dOqxYsSJLgyR6H1Z/ICIiIsoemWqgcPr0aTg6OqaZ37t3b5w+ffqzB0VERERERLlLphIHbW3t9y5TqVQaFRgQEAB7e3t89dVXqFu3Lm7evKnR9kRERERElP0ylTgkJCTgxYsXaeY/f/4c8fHxGhX47bffYsiQIbh9+zYmTJiAgQMHarQ9ERERERFlv0wlDl9//TV69+6NZ8+eKfOePXuG/v37o2fPnpkuLCIiAj4+PujVqxcAwNHREUFBQbh3755mURMRERERUbbKVOIwefJkmJiYoGTJkqhRowZq1KiBkiVLwtDQEFOmTMl0YQ8ePEDx4sWRL9/bNtkqlQqlSpVCcHDwx0VPRERERETZIlO9Kmlra2P9+vWYOnUqfHx8AAA1a9ZEuXLlNC4wdZsIEUmzzqJFi7Bo0SJlOjo6WuNysgJ71cl9+JsQZSw39D7Gc5WIvkS54fqZm2g0AFy5cuU+KllIVrJkSYSEhCAhIQH58uWDiODBgwcoVapUivVcXFzg4uKiTFtYWHx0mURERERE9OkyVVXpcylatChq1KiBTZs2AQDc3d1RpkwZlClTJjvDICIiIiIiDWn0xuFzWLVqFfr164c5c+bAyMgI69evz+4QiIjylC/h9TcREeV+2Z44WFlZ4dy5c9ldLBERERERfYJsrapERERERERfJiYORERERESUISYORERERESUISYORERERESUISYORERERESUISYORERERESUoWzvjpWIiIiI6EvAcXBS4hsHIiIiIiLKEBMHIiIiIiLKEBMHIiIiIiLKEBMHIiIiIiLKkEpEJKeDyEj+/PlRpEiRnA7jo0RHR0OtVud0GPQO/iZEGcst50luiYOISBNf8rXr8ePHiI2NTXfZF5E4fMksLCwQEhKS02HQO/ibEGUst5wnuSUOIiJN/FevXayqREREREREGWLiQEREREREGWLikMVcXFxyOgRKhb8JUcZyy3mSW+IgItLEf/Xa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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " STABILITY SELECTION : \n", - " Selected variables : intercept p__Bacteroidetes o__Acidobacteriales c__Acidobacteria-6 k__Bacteria \n", - " Running time : 44.955s\n", - "\n", - "['Life::k__Bacteria::p__Bacteroidetes'\n", - " 'Life::k__Bacteria::p__Acidobacteria::c__Acidobacteriia::o__Acidobacteriales'\n", - " 'Life::k__Bacteria::p__Acidobacteria::c__Acidobacteria-6'\n", - " 'Life::k__Bacteria']\n" - ] - } - ], + "outputs": [], "source": [ "problem = classo_problem(logGeom[tr], y[tr], label=label_short)\n", "\n", @@ -504,20 +301,9 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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1CkcP34c3bp1Q5MmTbJMH+ujUKFCCAkJwYgRI/D+++/Dx8fHoMdXqFABO3fuxOHDh3Hx4kX069cPd+/ezXCOn58fjh07huvXr+PBgwea0T4isi1M7IhIEt26dcPLly9Rp04dDBw4EIMHD0bfvn2zfUxAQAD27duHK1euoFGjRqhRowbGjh2rWXMGqKYyfX190aRJE4SEhKBv374oWrSozms6Oztjx44dKFq0KFq3bg1/f39MmzZNM3L4ySef4IMPPkCzZs3g7e2N1atXa8qlFCpUCI0bN0bz5s1Rrlw5rFmzJtdfj88//xypqano1auXwY8dO3YsatasiZYtW6Jp06aaRDW9r776CnK5HNWqVYO3tzfX3xHZKJkQmQorERGZWNOmTREUFJShzZe9i4qKwtChQ3H79m04OztLHQ4RWSlHqQMgIrJnL168wLVr1zB16lT069ePSR0R5QmnYomIJDRjxgwEBQWhWLFiGDVqlNThEJGV41QsERERkY3giB0RERGRjWBiR0RERGQjmNgRERER2QgmdkREREQ2gokdERERkY1gYkdERERkI5jYEREREdkIJnZERERENoKJHREREZGN+B+bUV3rLiseAAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "te = np.array([i for i in range(len(y)) if i not in tr])\n", "alpha = problem.solution.StabSel.refit\n", From b2bd2a5bbbbfcb73e16ea172b1347df1c497d85b Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Thu, 2 May 2024 18:34:25 +0200 Subject: [PATCH 03/28] good enough version - hopefully working --- experiments/implement_matrixA.ipynb | 1300 ++++++++++++++++++++++++++- experiments/test_classo.ipynb | 29 +- q2_ritme/process_data.py | 1 - 3 files changed, 1291 insertions(+), 39 deletions(-) diff --git a/experiments/implement_matrixA.ipynb b/experiments/implement_matrixA.ipynb index 8aebbd6..f90d3a4 100644 --- a/experiments/implement_matrixA.ipynb +++ b/experiments/implement_matrixA.ipynb @@ -7,16 +7,22 @@ "outputs": [], "source": [ "import numpy as np\n", - "from skbio import TreeNode\n", - "import qiime2 as q2\n", "import pandas as pd\n", + "import qiime2 as q2\n", "import skbio\n", - "from qiime2.plugins import phylogeny" + "from classo import classo_problem\n", + "from qiime2.plugins import phylogeny\n", + "from skbio import TreeNode\n", + "from q2_ritme.process_data import load_n_split_data\n", + "\n", + "%matplotlib inline\n", + "%load_ext autoreload\n", + "%autoreload 2" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -43,7 +49,11 @@ " # Populate A2 with 1s for the leaves linked by each internal node\n", " # iterate over all internal nodes to find descendents of this node and mark\n", " # them accordingly\n", + " a2_node_names = []\n", " for j, node in enumerate(internal_nodes):\n", + " # todo: adjust names to consensus taxonomy from descentents\n", + " # for now node names are just increasing integers - since node.name is float\n", + " a2_node_names.append(\"n\" + str(j))\n", " descendant_leaves = {leaf.name for leaf in node.tips()}\n", " for leaf_name in leaf_names:\n", " if leaf_name in descendant_leaves:\n", @@ -52,7 +62,7 @@ " # Concatenate A1 and A2 to create the final matrix A\n", " A = np.hstack((A1, A2))\n", "\n", - " return A" + " return A, a2_node_names" ] }, { @@ -100,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -111,14 +121,34 @@ " [0., 0., 1., 0.]])" ] }, - "execution_count": 4, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "A = create_matrix_from_tree(tree)\n", - "A" + "A_example, a2_names_ex = create_matrix_from_tree(tree)\n", + "A_example" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['n0']" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a2_names_ex" ] }, { @@ -130,7 +160,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -139,7 +169,7 @@ "(9478, 5580)" ] }, - "execution_count": 5, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -153,7 +183,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -178,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -187,7 +217,7 @@ "870198" ] }, - "execution_count": 7, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -203,7 +233,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -212,7 +242,7 @@ "11159" ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -229,7 +259,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -240,16 +270,9 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 24, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Root is not included\n" - ] - }, { "data": { "text/plain": [ @@ -262,31 +285,1244 @@ " [0., 0., 0., ..., 1., 1., 1.]])" ] }, - "execution_count": 17, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A, a2_names = create_matrix_from_tree(tree_phylo_f)\n", + "A" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['n0',\n", + " 'n1',\n", + " 'n2',\n", + " 'n3',\n", + " 'n4',\n", + " 'n5',\n", + " 'n6',\n", + " 'n7',\n", + " 'n8',\n", + " 'n9',\n", + " 'n10',\n", + " 'n11',\n", + " 'n12',\n", + " 'n13',\n", + " 'n14',\n", + " 'n15',\n", + " 'n16',\n", + " 'n17',\n", + " 'n18',\n", + " 'n19',\n", + " 'n20',\n", + " 'n21',\n", + " 'n22',\n", + " 'n23',\n", + " 'n24',\n", + " 'n25',\n", + " 'n26',\n", + " 'n27',\n", + " 'n28',\n", + " 'n29',\n", + " 'n30',\n", + " 'n31',\n", + " 'n32',\n", + " 'n33',\n", + " 'n34',\n", + " 'n35',\n", + " 'n36',\n", + " 'n37',\n", + " 'n38',\n", + " 'n39',\n", + " 'n40',\n", + " 'n41',\n", + " 'n42',\n", + " 'n43',\n", + " 'n44',\n", + " 'n45',\n", + " 'n46',\n", + " 'n47',\n", + " 'n48',\n", + " 'n49',\n", + " 'n50',\n", + " 'n51',\n", + " 'n52',\n", + " 'n53',\n", + " 'n54',\n", + " 'n55',\n", + " 'n56',\n", + " 'n57',\n", + " 'n58',\n", + " 'n59',\n", + " 'n60',\n", + " 'n61',\n", + " 'n62',\n", + " 'n63',\n", + " 'n64',\n", + " 'n65',\n", + " 'n66',\n", + " 'n67',\n", + " 'n68',\n", + " 'n69',\n", + " 'n70',\n", + " 'n71',\n", + " 'n72',\n", + " 'n73',\n", + " 'n74',\n", + " 'n75',\n", + " 'n76',\n", + " 'n77',\n", + " 'n78',\n", + " 'n79',\n", + " 'n80',\n", + " 'n81',\n", + " 'n82',\n", + " 'n83',\n", + " 'n84',\n", + " 'n85',\n", + " 'n86',\n", + " 'n87',\n", + " 'n88',\n", + " 'n89',\n", + " 'n90',\n", + " 'n91',\n", + " 'n92',\n", + " 'n93',\n", + " 'n94',\n", + " 'n95',\n", + " 'n96',\n", + " 'n97',\n", + " 'n98',\n", + " 'n99',\n", + " 'n100',\n", + " 'n101',\n", + " 'n102',\n", + " 'n103',\n", + " 'n104',\n", + " 'n105',\n", + " 'n106',\n", + " 'n107',\n", + " 'n108',\n", + " 'n109',\n", + " 'n110',\n", + " 'n111',\n", + " 'n112',\n", + " 'n113',\n", + " 'n114',\n", + " 'n115',\n", + " 'n116',\n", + " 'n117',\n", + " 'n118',\n", + " 'n119',\n", + " 'n120',\n", + " 'n121',\n", + " 'n122',\n", + " 'n123',\n", + " 'n124',\n", + " 'n125',\n", + " 'n126',\n", + " 'n127',\n", + " 'n128',\n", + " 'n129',\n", + " 'n130',\n", + " 'n131',\n", + " 'n132',\n", + " 'n133',\n", + " 'n134',\n", + " 'n135',\n", + " 'n136',\n", + " 'n137',\n", + " 'n138',\n", + " 'n139',\n", + " 'n140',\n", + " 'n141',\n", + " 'n142',\n", + " 'n143',\n", + " 'n144',\n", + " 'n145',\n", + " 'n146',\n", + " 'n147',\n", + " 'n148',\n", + " 'n149',\n", + " 'n150',\n", + " 'n151',\n", + " 'n152',\n", + " 'n153',\n", + " 'n154',\n", + " 'n155',\n", + " 'n156',\n", + " 'n157',\n", + " 'n158',\n", + " 'n159',\n", + " 'n160',\n", + " 'n161',\n", + " 'n162',\n", + " 'n163',\n", + " 'n164',\n", + " 'n165',\n", + " 'n166',\n", + " 'n167',\n", + " 'n168',\n", + " 'n169',\n", + " 'n170',\n", + " 'n171',\n", + " 'n172',\n", + " 'n173',\n", + " 'n174',\n", + " 'n175',\n", + " 'n176',\n", + " 'n177',\n", + " 'n178',\n", + " 'n179',\n", + " 'n180',\n", + " 'n181',\n", + " 'n182',\n", + " 'n183',\n", + " 'n184',\n", + " 'n185',\n", + " 'n186',\n", + " 'n187',\n", + " 'n188',\n", + " 'n189',\n", + " 'n190',\n", + " 'n191',\n", + " 'n192',\n", + " 'n193',\n", + " 'n194',\n", + " 'n195',\n", + " 'n196',\n", + " 'n197',\n", + " 'n198',\n", + " 'n199',\n", + " 'n200',\n", + " 'n201',\n", + " 'n202',\n", + " 'n203',\n", + " 'n204',\n", + " 'n205',\n", + " 'n206',\n", + " 'n207',\n", + " 'n208',\n", + " 'n209',\n", + " 'n210',\n", + " 'n211',\n", + " 'n212',\n", + " 'n213',\n", + " 'n214',\n", + " 'n215',\n", + " 'n216',\n", + " 'n217',\n", + " 'n218',\n", + " 'n219',\n", + " 'n220',\n", + " 'n221',\n", + " 'n222',\n", + " 'n223',\n", + " 'n224',\n", + " 'n225',\n", + " 'n226',\n", + " 'n227',\n", + " 'n228',\n", + " 'n229',\n", + " 'n230',\n", + " 'n231',\n", + " 'n232',\n", + " 'n233',\n", + " 'n234',\n", + " 'n235',\n", + " 'n236',\n", + " 'n237',\n", + " 'n238',\n", + " 'n239',\n", + " 'n240',\n", + " 'n241',\n", + " 'n242',\n", + " 'n243',\n", + " 'n244',\n", + " 'n245',\n", + " 'n246',\n", + " 'n247',\n", + " 'n248',\n", + " 'n249',\n", + " 'n250',\n", + " 'n251',\n", + " 'n252',\n", + " 'n253',\n", + " 'n254',\n", + " 'n255',\n", + " 'n256',\n", + " 'n257',\n", + " 'n258',\n", + " 'n259',\n", + " 'n260',\n", + " 'n261',\n", + " 'n262',\n", + " 'n263',\n", + " 'n264',\n", + " 'n265',\n", + " 'n266',\n", + " 'n267',\n", + " 'n268',\n", + " 'n269',\n", + " 'n270',\n", + " 'n271',\n", + " 'n272',\n", + " 'n273',\n", + " 'n274',\n", + " 'n275',\n", + " 'n276',\n", + " 'n277',\n", + " 'n278',\n", + " 'n279',\n", + " 'n280',\n", + " 'n281',\n", + " 'n282',\n", + " 'n283',\n", + " 'n284',\n", + " 'n285',\n", + " 'n286',\n", + " 'n287',\n", + " 'n288',\n", + " 'n289',\n", + " 'n290',\n", + " 'n291',\n", + " 'n292',\n", + " 'n293',\n", + " 'n294',\n", + " 'n295',\n", + " 'n296',\n", + " 'n297',\n", + " 'n298',\n", + " 'n299',\n", + " 'n300',\n", + " 'n301',\n", + " 'n302',\n", + " 'n303',\n", + " 'n304',\n", + " 'n305',\n", + " 'n306',\n", + " 'n307',\n", + " 'n308',\n", + " 'n309',\n", + " 'n310',\n", + " 'n311',\n", + " 'n312',\n", + " 'n313',\n", + " 'n314',\n", + " 'n315',\n", + " 'n316',\n", + " 'n317',\n", + " 'n318',\n", + " 'n319',\n", + " 'n320',\n", + " 'n321',\n", + " 'n322',\n", + " 'n323',\n", + " 'n324',\n", + " 'n325',\n", + " 'n326',\n", + " 'n327',\n", + " 'n328',\n", + " 'n329',\n", + " 'n330',\n", + " 'n331',\n", + " 'n332',\n", + " 'n333',\n", + " 'n334',\n", + " 'n335',\n", + " 'n336',\n", + " 'n337',\n", + " 'n338',\n", + " 'n339',\n", + " 'n340',\n", + " 'n341',\n", + " 'n342',\n", + " 'n343',\n", + " 'n344',\n", + " 'n345',\n", + " 'n346',\n", + " 'n347',\n", + " 'n348',\n", + " 'n349',\n", + " 'n350',\n", + " 'n351',\n", + " 'n352',\n", + " 'n353',\n", + " 'n354',\n", + " 'n355',\n", + " 'n356',\n", + " 'n357',\n", + " 'n358',\n", + " 'n359',\n", + " 'n360',\n", + " 'n361',\n", + " 'n362',\n", + " 'n363',\n", + " 'n364',\n", + " 'n365',\n", + " 'n366',\n", + " 'n367',\n", + " 'n368',\n", + " 'n369',\n", + " 'n370',\n", + " 'n371',\n", + " 'n372',\n", + " 'n373',\n", + " 'n374',\n", + " 'n375',\n", + " 'n376',\n", + " 'n377',\n", + " 'n378',\n", + " 'n379',\n", + " 'n380',\n", + " 'n381',\n", + " 'n382',\n", + " 'n383',\n", + " 'n384',\n", + " 'n385',\n", + " 'n386',\n", + " 'n387',\n", + " 'n388',\n", + " 'n389',\n", + " 'n390',\n", + " 'n391',\n", + " 'n392',\n", + " 'n393',\n", + " 'n394',\n", + " 'n395',\n", + " 'n396',\n", + " 'n397',\n", + " 'n398',\n", + " 'n399',\n", + " 'n400',\n", + " 'n401',\n", + " 'n402',\n", + " 'n403',\n", + " 'n404',\n", + " 'n405',\n", + " 'n406',\n", + " 'n407',\n", + " 'n408',\n", + " 'n409',\n", + " 'n410',\n", + " 'n411',\n", + " 'n412',\n", + " 'n413',\n", + " 'n414',\n", + " 'n415',\n", + " 'n416',\n", + " 'n417',\n", + " 'n418',\n", + " 'n419',\n", + " 'n420',\n", + " 'n421',\n", + " 'n422',\n", + " 'n423',\n", + " 'n424',\n", + " 'n425',\n", + " 'n426',\n", + " 'n427',\n", + " 'n428',\n", + " 'n429',\n", + " 'n430',\n", + " 'n431',\n", + " 'n432',\n", + " 'n433',\n", + " 'n434',\n", + " 'n435',\n", + " 'n436',\n", + " 'n437',\n", + " 'n438',\n", + " 'n439',\n", + " 'n440',\n", + " 'n441',\n", + " 'n442',\n", + " 'n443',\n", + " 'n444',\n", + " 'n445',\n", + " 'n446',\n", + " 'n447',\n", + " 'n448',\n", + " 'n449',\n", + " 'n450',\n", + " 'n451',\n", + " 'n452',\n", + " 'n453',\n", + " 'n454',\n", + " 'n455',\n", + " 'n456',\n", + " 'n457',\n", + " 'n458',\n", + " 'n459',\n", + " 'n460',\n", + " 'n461',\n", + " 'n462',\n", + " 'n463',\n", + " 'n464',\n", + " 'n465',\n", + " 'n466',\n", + " 'n467',\n", + " 'n468',\n", + " 'n469',\n", + " 'n470',\n", + " 'n471',\n", + " 'n472',\n", + " 'n473',\n", + " 'n474',\n", + " 'n475',\n", + " 'n476',\n", + " 'n477',\n", + " 'n478',\n", + " 'n479',\n", + " 'n480',\n", + " 'n481',\n", + " 'n482',\n", + " 'n483',\n", + " 'n484',\n", + " 'n485',\n", + " 'n486',\n", + " 'n487',\n", + " 'n488',\n", + " 'n489',\n", + " 'n490',\n", + " 'n491',\n", + " 'n492',\n", + " 'n493',\n", + " 'n494',\n", + " 'n495',\n", + " 'n496',\n", + " 'n497',\n", + " 'n498',\n", + " 'n499',\n", + " 'n500',\n", + " 'n501',\n", + " 'n502',\n", + " 'n503',\n", + " 'n504',\n", + " 'n505',\n", + " 'n506',\n", + " 'n507',\n", + " 'n508',\n", + " 'n509',\n", + " 'n510',\n", + " 'n511',\n", + " 'n512',\n", + " 'n513',\n", + " 'n514',\n", + " 'n515',\n", + " 'n516',\n", + " 'n517',\n", + " 'n518',\n", + " 'n519',\n", + " 'n520',\n", + " 'n521',\n", + " 'n522',\n", + " 'n523',\n", + " 'n524',\n", + " 'n525',\n", + " 'n526',\n", + " 'n527',\n", + " 'n528',\n", + " 'n529',\n", + " 'n530',\n", + " 'n531',\n", + " 'n532',\n", + " 'n533',\n", + " 'n534',\n", + " 'n535',\n", + " 'n536',\n", + " 'n537',\n", + " 'n538',\n", + " 'n539',\n", + " 'n540',\n", + " 'n541',\n", + " 'n542',\n", + " 'n543',\n", + " 'n544',\n", + " 'n545',\n", + " 'n546',\n", + " 'n547',\n", + " 'n548',\n", + " 'n549',\n", + " 'n550',\n", + " 'n551',\n", + " 'n552',\n", + " 'n553',\n", + " 'n554',\n", + " 'n555',\n", + " 'n556',\n", + " 'n557',\n", + " 'n558',\n", + " 'n559',\n", + " 'n560',\n", + " 'n561',\n", + " 'n562',\n", + " 'n563',\n", + " 'n564',\n", + " 'n565',\n", + " 'n566',\n", + " 'n567',\n", + " 'n568',\n", + " 'n569',\n", + " 'n570',\n", + " 'n571',\n", + " 'n572',\n", + " 'n573',\n", + " 'n574',\n", + " 'n575',\n", + " 'n576',\n", + " 'n577',\n", + " 'n578',\n", + " 'n579',\n", + " 'n580',\n", + " 'n581',\n", + " 'n582',\n", + " 'n583',\n", + " 'n584',\n", + " 'n585',\n", + " 'n586',\n", + " 'n587',\n", + " 'n588',\n", + " 'n589',\n", + " 'n590',\n", + " 'n591',\n", + " 'n592',\n", + " 'n593',\n", + " 'n594',\n", + " 'n595',\n", + " 'n596',\n", + " 'n597',\n", + " 'n598',\n", + " 'n599',\n", + " 'n600',\n", + " 'n601',\n", + " 'n602',\n", + " 'n603',\n", + " 'n604',\n", + " 'n605',\n", + " 'n606',\n", + " 'n607',\n", + " 'n608',\n", + " 'n609',\n", + " 'n610',\n", + " 'n611',\n", + " 'n612',\n", + " 'n613',\n", + " 'n614',\n", + " 'n615',\n", + " 'n616',\n", + " 'n617',\n", + " 'n618',\n", + " 'n619',\n", + " 'n620',\n", + " 'n621',\n", + " 'n622',\n", + " 'n623',\n", + " 'n624',\n", + " 'n625',\n", + " 'n626',\n", + " 'n627',\n", + " 'n628',\n", + " 'n629',\n", + " 'n630',\n", + " 'n631',\n", + " 'n632',\n", + " 'n633',\n", + " 'n634',\n", + " 'n635',\n", + " 'n636',\n", + " 'n637',\n", + " 'n638',\n", + " 'n639',\n", + " 'n640',\n", + " 'n641',\n", + " 'n642',\n", + " 'n643',\n", + " 'n644',\n", + " 'n645',\n", + " 'n646',\n", + " 'n647',\n", + " 'n648',\n", + " 'n649',\n", + " 'n650',\n", + " 'n651',\n", + " 'n652',\n", + " 'n653',\n", + " 'n654',\n", + " 'n655',\n", + " 'n656',\n", + " 'n657',\n", + " 'n658',\n", + " 'n659',\n", + " 'n660',\n", + " 'n661',\n", + " 'n662',\n", + " 'n663',\n", + " 'n664',\n", + " 'n665',\n", + " 'n666',\n", + " 'n667',\n", + " 'n668',\n", + " 'n669',\n", + " 'n670',\n", + " 'n671',\n", + " 'n672',\n", + " 'n673',\n", + " 'n674',\n", + " 'n675',\n", + " 'n676',\n", + " 'n677',\n", + " 'n678',\n", + " 'n679',\n", + " 'n680',\n", + " 'n681',\n", + " 'n682',\n", + " 'n683',\n", + " 'n684',\n", + " 'n685',\n", + " 'n686',\n", + " 'n687',\n", + " 'n688',\n", + " 'n689',\n", + " 'n690',\n", + " 'n691',\n", + " 'n692',\n", + " 'n693',\n", + " 'n694',\n", + " 'n695',\n", + " 'n696',\n", + " 'n697',\n", + " 'n698',\n", + " 'n699',\n", + " 'n700',\n", + " 'n701',\n", + " 'n702',\n", + " 'n703',\n", + " 'n704',\n", + " 'n705',\n", + " 'n706',\n", + " 'n707',\n", + " 'n708',\n", + " 'n709',\n", + " 'n710',\n", + " 'n711',\n", + " 'n712',\n", + " 'n713',\n", + " 'n714',\n", + " 'n715',\n", + " 'n716',\n", + " 'n717',\n", + " 'n718',\n", + " 'n719',\n", + " 'n720',\n", + " 'n721',\n", + " 'n722',\n", + " 'n723',\n", + " 'n724',\n", + " 'n725',\n", + " 'n726',\n", + " 'n727',\n", + " 'n728',\n", + " 'n729',\n", + " 'n730',\n", + " 'n731',\n", + " 'n732',\n", + " 'n733',\n", + " 'n734',\n", + " 'n735',\n", + " 'n736',\n", + " 'n737',\n", + " 'n738',\n", + " 'n739',\n", + " 'n740',\n", + " 'n741',\n", + " 'n742',\n", + " 'n743',\n", + " 'n744',\n", + " 'n745',\n", + " 'n746',\n", + " 'n747',\n", + " 'n748',\n", + " 'n749',\n", + " 'n750',\n", + " 'n751',\n", + " 'n752',\n", + " 'n753',\n", + " 'n754',\n", + " 'n755',\n", + " 'n756',\n", + " 'n757',\n", + " 'n758',\n", + " 'n759',\n", + " 'n760',\n", + " 'n761',\n", + " 'n762',\n", + " 'n763',\n", + " 'n764',\n", + " 'n765',\n", + " 'n766',\n", + " 'n767',\n", + " 'n768',\n", + " 'n769',\n", + " 'n770',\n", + " 'n771',\n", + " 'n772',\n", + " 'n773',\n", + " 'n774',\n", + " 'n775',\n", + " 'n776',\n", + " 'n777',\n", + " 'n778',\n", + " 'n779',\n", + " 'n780',\n", + " 'n781',\n", + " 'n782',\n", + " 'n783',\n", + " 'n784',\n", + " 'n785',\n", + " 'n786',\n", + " 'n787',\n", + " 'n788',\n", + " 'n789',\n", + " 'n790',\n", + " 'n791',\n", + " 'n792',\n", + " 'n793',\n", + " 'n794',\n", + " 'n795',\n", + " 'n796',\n", + " 'n797',\n", + " 'n798',\n", + " 'n799',\n", + " 'n800',\n", + " 'n801',\n", + " 'n802',\n", + " 'n803',\n", + " 'n804',\n", + " 'n805',\n", + " 'n806',\n", + " 'n807',\n", + " 'n808',\n", + " 'n809',\n", + " 'n810',\n", + " 'n811',\n", + " 'n812',\n", + " 'n813',\n", + " 'n814',\n", + " 'n815',\n", + " 'n816',\n", + " 'n817',\n", + " 'n818',\n", + " 'n819',\n", + " 'n820',\n", + " 'n821',\n", + " 'n822',\n", + " 'n823',\n", + " 'n824',\n", + " 'n825',\n", + " 'n826',\n", + " 'n827',\n", + " 'n828',\n", + " 'n829',\n", + " 'n830',\n", + " 'n831',\n", + " 'n832',\n", + " 'n833',\n", + " 'n834',\n", + " 'n835',\n", + " 'n836',\n", + " 'n837',\n", + " 'n838',\n", + " 'n839',\n", + " 'n840',\n", + " 'n841',\n", + " 'n842',\n", + " 'n843',\n", + " 'n844',\n", + " 'n845',\n", + " 'n846',\n", + " 'n847',\n", + " 'n848',\n", + " 'n849',\n", + " 'n850',\n", + " 'n851',\n", + " 'n852',\n", + " 'n853',\n", + " 'n854',\n", + " 'n855',\n", + " 'n856',\n", + " 'n857',\n", + " 'n858',\n", + " 'n859',\n", + " 'n860',\n", + " 'n861',\n", + " 'n862',\n", + " 'n863',\n", + " 'n864',\n", + " 'n865',\n", + " 'n866',\n", + " 'n867',\n", + " 'n868',\n", + " 'n869',\n", + " 'n870',\n", + " 'n871',\n", + " 'n872',\n", + " 'n873',\n", + " 'n874',\n", + " 'n875',\n", + " 'n876',\n", + " 'n877',\n", + " 'n878',\n", + " 'n879',\n", + " 'n880',\n", + " 'n881',\n", + " 'n882',\n", + " 'n883',\n", + " 'n884',\n", + " 'n885',\n", + " 'n886',\n", + " 'n887',\n", + " 'n888',\n", + " 'n889',\n", + " 'n890',\n", + " 'n891',\n", + " 'n892',\n", + " 'n893',\n", + " 'n894',\n", + " 'n895',\n", + " 'n896',\n", + " 'n897',\n", + " 'n898',\n", + " 'n899',\n", + " 'n900',\n", + " 'n901',\n", + " 'n902',\n", + " 'n903',\n", + " 'n904',\n", + " 'n905',\n", + " 'n906',\n", + " 'n907',\n", + " 'n908',\n", + " 'n909',\n", + " 'n910',\n", + " 'n911',\n", + " 'n912',\n", + " 'n913',\n", + " 'n914',\n", + " 'n915',\n", + " 'n916',\n", + " 'n917',\n", + " 'n918',\n", + " 'n919',\n", + " 'n920',\n", + " 'n921',\n", + " 'n922',\n", + " 'n923',\n", + " 'n924',\n", + " 'n925',\n", + " 'n926',\n", + " 'n927',\n", + " 'n928',\n", + " 'n929',\n", + " 'n930',\n", + " 'n931',\n", + " 'n932',\n", + " 'n933',\n", + " 'n934',\n", + " 'n935',\n", + " 'n936',\n", + " 'n937',\n", + " 'n938',\n", + " 'n939',\n", + " 'n940',\n", + " 'n941',\n", + " 'n942',\n", + " 'n943',\n", + " 'n944',\n", + " 'n945',\n", + " 'n946',\n", + " 'n947',\n", + " 'n948',\n", + " 'n949',\n", + " 'n950',\n", + " 'n951',\n", + " 'n952',\n", + " 'n953',\n", + " 'n954',\n", + " 'n955',\n", + " 'n956',\n", + " 'n957',\n", + " 'n958',\n", + " 'n959',\n", + " 'n960',\n", + " 'n961',\n", + " 'n962',\n", + " 'n963',\n", + " 'n964',\n", + " 'n965',\n", + " 'n966',\n", + " 'n967',\n", + " 'n968',\n", + " 'n969',\n", + " 'n970',\n", + " 'n971',\n", + " 'n972',\n", + " 'n973',\n", + " 'n974',\n", + " 'n975',\n", + " 'n976',\n", + " 'n977',\n", + " 'n978',\n", + " 'n979',\n", + " 'n980',\n", + " 'n981',\n", + " 'n982',\n", + " 'n983',\n", + " 'n984',\n", + " 'n985',\n", + " 'n986',\n", + " 'n987',\n", + " 'n988',\n", + " 'n989',\n", + " 'n990',\n", + " 'n991',\n", + " 'n992',\n", + " 'n993',\n", + " 'n994',\n", + " 'n995',\n", + " 'n996',\n", + " 'n997',\n", + " 'n998',\n", + " 'n999',\n", + " ...]" + ] + }, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "A_ma2 = create_matrix_from_tree(tree_phylo_f)\n", - "A_ma2" + "a2_names" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "# verififcation\n", + "# verification\n", "# no all 1 in one column\n", - "assert not np.any(np.all(A_ma2 == 1.0, axis=0))\n", + "assert not np.any(np.all(A == 1.0, axis=0))\n", "\n", "# shape should be = feature_count + node_count\n", "nb_features = df_ft.shape[1]\n", "nb_non_leaf_nodes = len(list(tree_phylo_f.non_tips()))\n", "\n", - "assert nb_features + nb_non_leaf_nodes == A_ma2.shape[1]" + "assert nb_features + nb_non_leaf_nodes == A.shape[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run trac with this" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Feature columns do not sum to 1.0 for all samples - so they are being transformed.\n", + "Train: (3170, 5654), Test: (779, 5654)\n" + ] + } + ], + "source": [ + "# load metadata\n", + "target = \"age_months\"\n", + "train_val, test = load_n_split_data(\n", + " path2md=\"data/220728_monthly/metadata_proc_v20240323_r0_r3_le_2yrs.tsv\",\n", + " path2ft=\"data/220728_monthly/all_otu_table_filt.qza\",\n", + " host_id=\"host_id\",\n", + " target=target,\n", + " train_size=0.8,\n", + " seed=12,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "# preprocess taxonomy aggregation\n", + "def _preprocess_taxonomy_aggregation(x, A):\n", + " pseudo_count = 0.000001\n", + " # ? what happens if x is relative abundances\n", + " X = np.log(pseudo_count + x)\n", + " nleaves = np.sum(A, axis=0)\n", + " log_geom = X.dot(A) / nleaves\n", + "\n", + " return log_geom, nleaves" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "# perform preprocessing on train\n", + "ft_cols = [x for x in train_val.columns if x.startswith(\"F\")]\n", + "x_train_val = train_val[ft_cols]\n", + "y_train_val = train_val[target]\n", + "# todo: afterwards perform it on test\n", + "log_geom_trainval, nleaves = _preprocess_taxonomy_aggregation(x_train_val.values, A)\n", + "\n", + "n, d = log_geom_trainval.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([' g__Fusobacterium', ' g__Rheinheimera',\n", + " ' s__uncultured_bacterium', ..., 'n5575', 'n5576', 'n5577'],\n", + " dtype=' for now it's just n + count\n", + "label = df_taxonomy_f[\"Taxon\"].values\n", + "label_short = np.array([la.split(\";\")[-1] for la in label])\n", + "assert len(label) == len(ft_cols)\n", + "assert len(label) == len(label_short)\n", + "label = np.append(label, a2_names)\n", + "label_short = np.append(label_short, a2_names)\n", + "\n", + "assert len(label_short) == A.shape[1]\n", + "label_short" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + " \n", + "FORMULATION: R1\n", + " \n", + "MODEL SELECTION COMPUTED: \n", + " Cross Validation\n", + " \n", + "CROSS VALIDATION PARAMETERS: \n", + " numerical_method : not specified\n", + " one-SE method : True\n", + " Nsubset = 5\n", + " lamin = 0.001\n", + " Nlam = 80\n", + " with log-scale\n", + "\n" + ] + } + ], + "source": [ + "# perform CV classo: trac\n", + "problem = classo_problem(log_geom_trainval, y_train_val.values, label=label_short)\n", + "\n", + "problem.formulation.w = 1 / nleaves\n", + "problem.formulation.intercept = True\n", + "problem.formulation.concomitant = False # not relevant for here\n", + "\n", + "# ! one form of model selection needs to be chosen\n", + "# stability selection: for pre-selected range of lambda find beta paths\n", + "problem.model_selection.StabSel = False\n", + "# calculate coefficients for a grid of lambdas\n", + "problem.model_selection.PATH = False\n", + "# todo: check if it is fair that trac is trained with CV internally whereas others are not\n", + "# lambda values checked with CV are `Nlam` points between 1 and `lamin`, with\n", + "# logarithm scale or not depending on `logscale`.\n", + "problem.model_selection.CV = True\n", + "problem.model_selection.CVparameters.seed = (\n", + " 6 # one could change logscale, Nsubset, oneSE\n", + ")\n", + "# 'one-standard-error' = select simplest model (largest lambda value) in CV\n", + "# whose CV score is within 1 stddev of best score\n", + "# ! create hyperparameter for this\n", + "problem.model_selection.CVparameters.oneSE = True\n", + "# ! create hyperparameter for this\n", + "problem.model_selection.CVparameters.Nlam = 80\n", + "# ! create hyperparameter for this\n", + "problem.model_selection.CVparameters.lamin = 0.001\n", + "\n", + "# ! for ritme: no feature_transformation to be used for trac\n", + "print(problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[37], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mproblem\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msolve\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(problem\u001b[38;5;241m.\u001b[39msolution)\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/solver.py:153\u001b[0m, in \u001b[0;36mclasso_problem.solve\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 151\u001b[0m \u001b[38;5;66;03m# Compute the cross validation thanks to the class solution_CV which contains directely the computation in the initialisation\u001b[39;00m\n\u001b[1;32m 152\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmodel_selection\u001b[38;5;241m.\u001b[39mCV:\n\u001b[0;32m--> 153\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msolution\u001b[38;5;241m.\u001b[39mCV \u001b[38;5;241m=\u001b[39m \u001b[43msolution_CV\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 154\u001b[0m \u001b[43m \u001b[49m\u001b[43mmatrices\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 155\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel_selection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCVparameters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 156\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformulation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 157\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnumerical_method\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 158\u001b[0m \u001b[43m \u001b[49m\u001b[43mlabel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 159\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 161\u001b[0m \u001b[38;5;66;03m# Compute the Stability Selection thanks to the class solution_SS which contains directely the computation in the initialisation\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmodel_selection\u001b[38;5;241m.\u001b[39mStabSel:\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/solver.py:985\u001b[0m, in \u001b[0;36msolution_CV.__init__\u001b[0;34m(self, matrices, param, formulation, numerical_method, label)\u001b[0m\n\u001b[1;32m 982\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlogscale \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mlogscale\n\u001b[1;32m 984\u001b[0m \u001b[38;5;66;03m# Compute the solution and is the formulation is concomitant, it also compute sigma\u001b[39;00m\n\u001b[0;32m--> 985\u001b[0m (out, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39myGraph, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstandard_error, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindex_min, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindex_1SE,) \u001b[38;5;241m=\u001b[39m \u001b[43mCV\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mmatrices\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mNsubset\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mtyp\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mname_formulation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mnum_meth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnumerical_method\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43mlambdas\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlambdas\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 991\u001b[0m \u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mseed\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 992\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 993\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho_classification\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho_classification\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 994\u001b[0m \u001b[43m \u001b[49m\u001b[43moneSE\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moneSE\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 995\u001b[0m \u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 996\u001b[0m \u001b[43m \u001b[49m\u001b[43mw\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformulation\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 997\u001b[0m \u001b[43m \u001b[49m\u001b[43mintercept\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformulation\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mintercept\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 998\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1000\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mxGraph \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mlambdas\n\u001b[1;32m 1001\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlambda_1SE \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mlambdas[\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindex_1SE]\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/cross_validation.py:176\u001b[0m, in \u001b[0;36mCV\u001b[0;34m(matrices, k, typ, num_meth, seed, rho, rho_classification, e, lambdas, Nlam, oneSE, w, intercept)\u001b[0m\n\u001b[1;32m 174\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 175\u001b[0m lam \u001b[38;5;241m=\u001b[39m lambdas[i]\n\u001b[0;32m--> 176\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[43mClasso\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 177\u001b[0m \u001b[43m \u001b[49m\u001b[43mmatrices\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 178\u001b[0m \u001b[43m \u001b[49m\u001b[43mlam\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 179\u001b[0m \u001b[43m \u001b[49m\u001b[43mtyp\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtyp\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 180\u001b[0m \u001b[43m \u001b[49m\u001b[43mmeth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnum_meth\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 181\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 182\u001b[0m \u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 183\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho_classification\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho_classification\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 184\u001b[0m \u001b[43m \u001b[49m\u001b[43mw\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 185\u001b[0m \u001b[43m \u001b[49m\u001b[43mintercept\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mintercept\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 186\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 187\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m (out, MSE, SE, i, i_1SE)\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/compact_func.py:163\u001b[0m, in \u001b[0;36mClasso\u001b[0;34m(matrix, lam, typ, meth, rho, get_lambdamax, true_lam, e, rho_classification, w, intercept, return_sigm)\u001b[0m\n\u001b[1;32m 161\u001b[0m beta \u001b[38;5;241m=\u001b[39m Classo_R1(pb, lam \u001b[38;5;241m/\u001b[39m lambdamax)\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 163\u001b[0m beta \u001b[38;5;241m=\u001b[39m \u001b[43mClasso_R1\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlam\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 165\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m intercept:\n\u001b[1;32m 166\u001b[0m betaO \u001b[38;5;241m=\u001b[39m ybar \u001b[38;5;241m-\u001b[39m np\u001b[38;5;241m.\u001b[39mvdot(Xbar, beta)\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/solve_R1.py:28\u001b[0m, in \u001b[0;36mClasso_R1\u001b[0;34m(pb, lam)\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[38;5;66;03m# ODE\u001b[39;00m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;66;03m# here we compute the path algo until our lambda, and just take the last beta\u001b[39;00m\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m pb_type \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPath-Alg\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m---> 28\u001b[0m BETA \u001b[38;5;241m=\u001b[39m \u001b[43msolve_path\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpb\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmatrix\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlam\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mR1\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m 29\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m BETA[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]\n\u001b[1;32m 31\u001b[0m regpath \u001b[38;5;241m=\u001b[39m pb\u001b[38;5;241m.\u001b[39mregpath\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:169\u001b[0m, in \u001b[0;36msolve_path\u001b[0;34m(matrices, lamin, n_active, rho, typ, intercept)\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m BETA, LAM\n\u001b[1;32m 161\u001b[0m \u001b[38;5;66;03m# elif not np.any(param.F):\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;66;03m# print(param.r)\u001b[39;00m\n\u001b[1;32m 163\u001b[0m \u001b[38;5;66;03m# raise ValueError(\"The problem looks infeasible because the set of active sample became zero, \"\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 166\u001b[0m \u001b[38;5;66;03m# \"with intercept ? {} \"\u001b[39;00m\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# \"with rho equal to {} \".format(i, typ, intercept, rho ))\u001b[39;00m\n\u001b[0;32m--> 169\u001b[0m \u001b[43mup\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparam\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 170\u001b[0m BETA\u001b[38;5;241m.\u001b[39mappend(param\u001b[38;5;241m.\u001b[39mbeta), LAM\u001b[38;5;241m.\u001b[39mappend(param\u001b[38;5;241m.\u001b[39mlam)\n\u001b[1;32m 172\u001b[0m \u001b[38;5;66;03m# print(\"inside : \", param.r[ param.F], np.nonzero(param.F)[0] )\u001b[39;00m\n\u001b[1;32m 173\u001b[0m \u001b[38;5;66;03m# print(\" outside : \", param.r[~param.F], np.nonzero(~param.F)[0])\u001b[39;00m\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:291\u001b[0m, in \u001b[0;36mup\u001b[0;34m(param)\u001b[0m\n\u001b[1;32m 289\u001b[0m formulation \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mformulation\n\u001b[1;32m 290\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m formulation \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mR1\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mR3\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[0;32m--> 291\u001b[0m \u001b[43mup_LS\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparam\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 292\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m formulation \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mR2\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 293\u001b[0m up_huber(param)\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:327\u001b[0m, in \u001b[0;36mup_LS\u001b[0;34m(param)\u001b[0m\n\u001b[1;32m 325\u001b[0m L \u001b[38;5;241m=\u001b[39m [lam] \u001b[38;5;241m*\u001b[39m d\n\u001b[1;32m 326\u001b[0m Mat \u001b[38;5;241m=\u001b[39m M[:d, :d]\n\u001b[0;32m--> 327\u001b[0m beta_dot, lam_s_dot \u001b[38;5;241m=\u001b[39m \u001b[43mderivatives\u001b[49m\u001b[43m(\u001b[49m\u001b[43mactivity\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43ms\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mMat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mM\u001b[49m\u001b[43m[\u001b[49m\u001b[43md\u001b[49m\u001b[43m:\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m:\u001b[49m\u001b[43md\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mXt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43midr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumber_act\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 328\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(d):\n\u001b[1;32m 329\u001b[0m bi, di, e, s0 \u001b[38;5;241m=\u001b[39m beta[i], beta_dot[i], lam_s_dot[i], s[i]\n", + "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:784\u001b[0m, in \u001b[0;36mderivatives\u001b[0;34m(activity, s, Mat, C, Inv, idr, number_act)\u001b[0m\n\u001b[1;32m 782\u001b[0m beta_dot \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mzeros(\u001b[38;5;28mlen\u001b[39m(activity))\n\u001b[1;32m 783\u001b[0m beta_dot[activity] \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m-\u001b[39mInv[:number_act, :number_act]\u001b[38;5;241m.\u001b[39mdot(s[activity])\n\u001b[0;32m--> 784\u001b[0m lam_s_dot \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m-\u001b[39m\u001b[43mMat\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mbeta_dot\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 786\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(C) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 787\u001b[0m v_dot \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mzeros(\u001b[38;5;28mlen\u001b[39m(C))\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "problem.solve()\n", + "print(problem.solution)" ] } ], diff --git a/experiments/test_classo.ipynb b/experiments/test_classo.ipynb index 0f95f5b..5bc6922 100644 --- a/experiments/test_classo.ipynb +++ b/experiments/test_classo.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -40,14 +40,14 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "data_dir = join(\"data\", \"CentralParkSoil\")\n", "data = np.load(join(data_dir, \"cps.npz\"))\n", "\n", - "# X are relative abundances\n", + "# X are relative abundance counts\n", "x = data[\"x\"] # (580, 3379)\n", "\n", "# y is target\n", @@ -59,11 +59,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 0, 0, 1, ..., 0, 0, 0],\n", + " [ 0, 4, 0, ..., 16, 31, 0],\n", + " [ 0, 0, 7, ..., 0, 0, 0],\n", + " ...,\n", + " [ 0, 0, 2, ..., 0, 0, 2],\n", + " [ 4, 0, 0, ..., 8, 4, 0],\n", + " [ 1, 7, 0, ..., 1, 6, 0]], dtype=int32)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "label" + "x" ] }, { diff --git a/q2_ritme/process_data.py b/q2_ritme/process_data.py index a282aa1..07ff692 100644 --- a/q2_ritme/process_data.py +++ b/q2_ritme/process_data.py @@ -37,7 +37,6 @@ def get_relative_abundance( columns=ft_rel_biom.ids(axis="observation"), ) - print(ft_rel.head()) # round needed as certain 1.0 are represented in different digits 2e-16 assert ft_rel[ft_cols].sum(axis=1).round(5).eq(1.0).all() From 659a60ac6d2a0dbc1606938c7c897f59dc4f0790 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Fri, 3 May 2024 15:54:26 +0200 Subject: [PATCH 04/28] working trac version to be implemented in ritme --- experiments/implement_matrixA.ipynb | 1341 +++------------------------ experiments/test_classo.ipynb | 116 ++- 2 files changed, 232 insertions(+), 1225 deletions(-) diff --git a/experiments/implement_matrixA.ipynb b/experiments/implement_matrixA.ipynb index f90d3a4..1ed0afb 100644 --- a/experiments/implement_matrixA.ipynb +++ b/experiments/implement_matrixA.ipynb @@ -2,11 +2,12 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", + "import os\n", "import pandas as pd\n", "import qiime2 as q2\n", "import skbio\n", @@ -22,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -74,21 +75,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " /-f1\n", - " /n1------|\n", - "-n2------| \\-f2\n", - " |\n", - " \\-f3\n" - ] - } - ], + "outputs": [], "source": [ "# Create the tree nodes with lengths\n", "n1 = TreeNode(name=\"n1\")\n", @@ -110,22 +99,9 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1., 0., 0., 1.],\n", - " [0., 1., 0., 1.],\n", - " [0., 0., 1., 0.]])" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "A_example, a2_names_ex = create_matrix_from_tree(tree)\n", "A_example" @@ -133,20 +109,9 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['n0']" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "a2_names_ex" ] @@ -160,20 +125,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(9478, 5580)" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# read feature table\n", "art_feature_table = q2.Artifact.load(\"data/220728_monthly/all_otu_table_filt.qza\")\n", @@ -183,18 +137,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(5608, 2)\n", - "(5580, 2)\n" - ] - } - ], + "outputs": [], "source": [ "path_to_taxonomy = \"data/220728_monthly/otu_taxonomy_all.qza\"\n", "art_taxonomy = q2.Artifact.load(path_to_taxonomy)\n", @@ -208,20 +153,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "870198" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# read silva phylo tree\n", "path_to_phylo = \"data/220728_monthly/silva-138-99-rooted-tree.qza\"\n", @@ -233,20 +167,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "11159" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# filter tree by feature table: this prunes a phylogenetic tree to match the\n", "# input ids\n", @@ -259,7 +182,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -270,26 +193,9 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1., 0., 0., ..., 0., 0., 0.],\n", - " [0., 1., 0., ..., 0., 0., 0.],\n", - " [0., 0., 1., ..., 0., 0., 0.],\n", - " ...,\n", - " [0., 0., 0., ..., 0., 1., 1.],\n", - " [0., 0., 0., ..., 1., 1., 1.],\n", - " [0., 0., 0., ..., 1., 1., 1.]])" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "A, a2_names = create_matrix_from_tree(tree_phylo_f)\n", "A" @@ -297,1027 +203,16 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['n0',\n", - " 'n1',\n", - " 'n2',\n", - " 'n3',\n", - " 'n4',\n", - " 'n5',\n", - " 'n6',\n", - " 'n7',\n", - " 'n8',\n", - " 'n9',\n", - " 'n10',\n", - " 'n11',\n", - " 'n12',\n", - " 'n13',\n", - " 'n14',\n", - " 'n15',\n", - " 'n16',\n", - " 'n17',\n", - " 'n18',\n", - " 'n19',\n", - " 'n20',\n", - " 'n21',\n", - " 'n22',\n", - " 'n23',\n", - " 'n24',\n", - " 'n25',\n", - " 'n26',\n", - " 'n27',\n", - " 'n28',\n", - " 'n29',\n", - " 'n30',\n", - " 'n31',\n", - " 'n32',\n", - " 'n33',\n", - " 'n34',\n", - " 'n35',\n", - " 'n36',\n", - " 'n37',\n", - " 'n38',\n", - " 'n39',\n", - " 'n40',\n", - " 'n41',\n", - " 'n42',\n", - " 'n43',\n", - " 'n44',\n", - " 'n45',\n", - " 'n46',\n", - " 'n47',\n", - " 'n48',\n", - " 'n49',\n", - " 'n50',\n", - " 'n51',\n", - " 'n52',\n", - " 'n53',\n", - " 'n54',\n", - " 'n55',\n", - " 'n56',\n", - " 'n57',\n", - " 'n58',\n", - " 'n59',\n", - " 'n60',\n", - " 'n61',\n", - " 'n62',\n", - " 'n63',\n", - " 'n64',\n", - " 'n65',\n", - " 'n66',\n", - " 'n67',\n", - " 'n68',\n", - " 'n69',\n", - " 'n70',\n", - " 'n71',\n", - " 'n72',\n", - " 'n73',\n", - " 'n74',\n", - " 'n75',\n", - " 'n76',\n", - " 'n77',\n", - " 'n78',\n", - " 'n79',\n", - " 'n80',\n", - " 'n81',\n", - " 'n82',\n", - " 'n83',\n", - " 'n84',\n", - " 'n85',\n", - " 'n86',\n", - " 'n87',\n", - " 'n88',\n", - " 'n89',\n", - " 'n90',\n", - " 'n91',\n", - " 'n92',\n", - " 'n93',\n", - " 'n94',\n", - " 'n95',\n", - " 'n96',\n", - " 'n97',\n", - " 'n98',\n", - " 'n99',\n", - " 'n100',\n", - " 'n101',\n", - " 'n102',\n", - " 'n103',\n", - " 'n104',\n", - " 'n105',\n", - " 'n106',\n", - " 'n107',\n", - " 'n108',\n", - " 'n109',\n", - " 'n110',\n", - " 'n111',\n", - " 'n112',\n", - " 'n113',\n", - " 'n114',\n", - " 'n115',\n", - " 'n116',\n", - " 'n117',\n", - " 'n118',\n", - " 'n119',\n", - " 'n120',\n", - " 'n121',\n", - " 'n122',\n", - " 'n123',\n", - " 'n124',\n", - " 'n125',\n", - " 'n126',\n", - " 'n127',\n", - " 'n128',\n", - " 'n129',\n", - " 'n130',\n", - " 'n131',\n", - " 'n132',\n", - " 'n133',\n", - " 'n134',\n", - " 'n135',\n", - " 'n136',\n", - " 'n137',\n", - " 'n138',\n", - " 'n139',\n", - " 'n140',\n", - " 'n141',\n", - " 'n142',\n", - " 'n143',\n", - " 'n144',\n", - " 'n145',\n", - " 'n146',\n", - " 'n147',\n", - " 'n148',\n", - " 'n149',\n", - " 'n150',\n", - " 'n151',\n", - " 'n152',\n", - " 'n153',\n", - " 'n154',\n", - " 'n155',\n", - " 'n156',\n", - " 'n157',\n", - " 'n158',\n", - " 'n159',\n", - " 'n160',\n", - " 'n161',\n", - " 'n162',\n", - " 'n163',\n", - " 'n164',\n", - " 'n165',\n", - " 'n166',\n", - " 'n167',\n", - " 'n168',\n", - " 'n169',\n", - " 'n170',\n", - " 'n171',\n", - " 'n172',\n", - " 'n173',\n", - " 'n174',\n", - " 'n175',\n", - " 'n176',\n", - " 'n177',\n", - " 'n178',\n", - " 'n179',\n", - " 'n180',\n", - " 'n181',\n", - " 'n182',\n", - " 'n183',\n", - " 'n184',\n", - " 'n185',\n", - " 'n186',\n", - " 'n187',\n", - " 'n188',\n", - " 'n189',\n", - " 'n190',\n", - " 'n191',\n", - " 'n192',\n", - " 'n193',\n", - " 'n194',\n", - " 'n195',\n", - " 'n196',\n", - " 'n197',\n", - " 'n198',\n", - " 'n199',\n", - " 'n200',\n", - " 'n201',\n", - " 'n202',\n", - " 'n203',\n", - " 'n204',\n", - " 'n205',\n", - " 'n206',\n", - " 'n207',\n", - " 'n208',\n", - " 'n209',\n", - " 'n210',\n", - " 'n211',\n", - " 'n212',\n", - " 'n213',\n", - " 'n214',\n", - " 'n215',\n", - " 'n216',\n", - " 'n217',\n", - " 'n218',\n", - " 'n219',\n", - " 'n220',\n", - " 'n221',\n", - " 'n222',\n", - " 'n223',\n", - " 'n224',\n", - " 'n225',\n", - " 'n226',\n", - " 'n227',\n", - " 'n228',\n", - " 'n229',\n", - " 'n230',\n", - " 'n231',\n", - " 'n232',\n", - " 'n233',\n", - " 'n234',\n", - " 'n235',\n", - " 'n236',\n", - " 'n237',\n", - " 'n238',\n", - " 'n239',\n", - " 'n240',\n", - " 'n241',\n", - " 'n242',\n", - " 'n243',\n", - " 'n244',\n", - " 'n245',\n", - " 'n246',\n", - " 'n247',\n", - " 'n248',\n", - " 'n249',\n", - " 'n250',\n", - " 'n251',\n", - " 'n252',\n", - " 'n253',\n", - " 'n254',\n", - " 'n255',\n", - " 'n256',\n", - " 'n257',\n", - " 'n258',\n", - " 'n259',\n", - " 'n260',\n", - " 'n261',\n", - " 'n262',\n", - " 'n263',\n", - " 'n264',\n", - " 'n265',\n", - " 'n266',\n", - " 'n267',\n", - " 'n268',\n", - " 'n269',\n", - " 'n270',\n", - " 'n271',\n", - " 'n272',\n", - " 'n273',\n", - " 'n274',\n", - " 'n275',\n", - " 'n276',\n", - " 'n277',\n", - " 'n278',\n", - " 'n279',\n", - " 'n280',\n", - " 'n281',\n", - " 'n282',\n", - " 'n283',\n", - " 'n284',\n", - " 'n285',\n", - " 'n286',\n", - " 'n287',\n", - " 'n288',\n", - " 'n289',\n", - " 'n290',\n", - " 'n291',\n", - " 'n292',\n", - " 'n293',\n", - " 'n294',\n", - " 'n295',\n", - " 'n296',\n", - " 'n297',\n", - " 'n298',\n", - " 'n299',\n", - " 'n300',\n", - " 'n301',\n", - " 'n302',\n", - " 'n303',\n", - " 'n304',\n", - " 'n305',\n", - " 'n306',\n", - " 'n307',\n", - " 'n308',\n", - " 'n309',\n", - " 'n310',\n", - " 'n311',\n", - " 'n312',\n", - " 'n313',\n", - " 'n314',\n", - " 'n315',\n", - " 'n316',\n", - " 'n317',\n", - " 'n318',\n", - " 'n319',\n", - " 'n320',\n", - " 'n321',\n", - " 'n322',\n", - " 'n323',\n", - " 'n324',\n", - " 'n325',\n", - " 'n326',\n", - " 'n327',\n", - " 'n328',\n", - " 'n329',\n", - " 'n330',\n", - " 'n331',\n", - " 'n332',\n", - " 'n333',\n", - " 'n334',\n", - " 'n335',\n", - " 'n336',\n", - " 'n337',\n", - " 'n338',\n", - " 'n339',\n", - " 'n340',\n", - " 'n341',\n", - " 'n342',\n", - " 'n343',\n", - " 'n344',\n", - " 'n345',\n", - " 'n346',\n", - " 'n347',\n", - " 'n348',\n", - " 'n349',\n", - " 'n350',\n", - " 'n351',\n", - " 'n352',\n", - " 'n353',\n", - " 'n354',\n", - " 'n355',\n", - " 'n356',\n", - " 'n357',\n", - " 'n358',\n", - " 'n359',\n", - " 'n360',\n", - " 'n361',\n", - " 'n362',\n", - " 'n363',\n", - " 'n364',\n", - " 'n365',\n", - " 'n366',\n", - " 'n367',\n", - " 'n368',\n", - " 'n369',\n", - " 'n370',\n", - " 'n371',\n", - " 'n372',\n", - " 'n373',\n", - " 'n374',\n", - " 'n375',\n", - " 'n376',\n", - " 'n377',\n", - " 'n378',\n", - " 'n379',\n", - " 'n380',\n", - " 'n381',\n", - " 'n382',\n", - " 'n383',\n", - " 'n384',\n", - " 'n385',\n", - " 'n386',\n", - " 'n387',\n", - " 'n388',\n", - " 'n389',\n", - " 'n390',\n", - " 'n391',\n", - " 'n392',\n", - " 'n393',\n", - " 'n394',\n", - " 'n395',\n", - " 'n396',\n", - " 'n397',\n", - " 'n398',\n", - " 'n399',\n", - " 'n400',\n", - " 'n401',\n", - " 'n402',\n", - " 'n403',\n", - " 'n404',\n", - " 'n405',\n", - " 'n406',\n", - " 'n407',\n", - " 'n408',\n", - " 'n409',\n", - " 'n410',\n", - " 'n411',\n", - " 'n412',\n", - " 'n413',\n", - " 'n414',\n", - " 'n415',\n", - " 'n416',\n", - " 'n417',\n", - " 'n418',\n", - " 'n419',\n", - " 'n420',\n", - " 'n421',\n", - " 'n422',\n", - " 'n423',\n", - " 'n424',\n", - " 'n425',\n", - " 'n426',\n", - " 'n427',\n", - " 'n428',\n", - " 'n429',\n", - " 'n430',\n", - " 'n431',\n", - " 'n432',\n", - " 'n433',\n", - " 'n434',\n", - " 'n435',\n", - " 'n436',\n", - " 'n437',\n", - " 'n438',\n", - " 'n439',\n", - " 'n440',\n", - " 'n441',\n", - " 'n442',\n", - " 'n443',\n", - " 'n444',\n", - " 'n445',\n", - " 'n446',\n", - " 'n447',\n", - " 'n448',\n", - " 'n449',\n", - " 'n450',\n", - " 'n451',\n", - " 'n452',\n", - " 'n453',\n", - " 'n454',\n", - " 'n455',\n", - " 'n456',\n", - " 'n457',\n", - " 'n458',\n", - " 'n459',\n", - " 'n460',\n", - " 'n461',\n", - " 'n462',\n", - " 'n463',\n", - " 'n464',\n", - " 'n465',\n", - " 'n466',\n", - " 'n467',\n", - " 'n468',\n", - " 'n469',\n", - " 'n470',\n", - " 'n471',\n", - " 'n472',\n", - " 'n473',\n", - " 'n474',\n", - " 'n475',\n", - " 'n476',\n", - " 'n477',\n", - " 'n478',\n", - " 'n479',\n", - " 'n480',\n", - " 'n481',\n", - " 'n482',\n", - " 'n483',\n", - " 'n484',\n", - " 'n485',\n", - " 'n486',\n", - " 'n487',\n", - " 'n488',\n", - " 'n489',\n", - " 'n490',\n", - " 'n491',\n", - " 'n492',\n", - " 'n493',\n", - " 'n494',\n", - " 'n495',\n", - " 'n496',\n", - " 'n497',\n", - " 'n498',\n", - " 'n499',\n", - " 'n500',\n", - " 'n501',\n", - " 'n502',\n", - " 'n503',\n", - " 'n504',\n", - " 'n505',\n", - " 'n506',\n", - " 'n507',\n", - " 'n508',\n", - " 'n509',\n", - " 'n510',\n", - " 'n511',\n", - " 'n512',\n", - " 'n513',\n", - " 'n514',\n", - " 'n515',\n", - " 'n516',\n", - " 'n517',\n", - " 'n518',\n", - " 'n519',\n", - " 'n520',\n", - " 'n521',\n", - " 'n522',\n", - " 'n523',\n", - " 'n524',\n", - " 'n525',\n", - " 'n526',\n", - " 'n527',\n", - " 'n528',\n", - " 'n529',\n", - " 'n530',\n", - " 'n531',\n", - " 'n532',\n", - " 'n533',\n", - " 'n534',\n", - " 'n535',\n", - " 'n536',\n", - " 'n537',\n", - " 'n538',\n", - " 'n539',\n", - " 'n540',\n", - " 'n541',\n", - " 'n542',\n", - " 'n543',\n", - " 'n544',\n", - " 'n545',\n", - " 'n546',\n", - " 'n547',\n", - " 'n548',\n", - " 'n549',\n", - " 'n550',\n", - " 'n551',\n", - " 'n552',\n", - " 'n553',\n", - " 'n554',\n", - " 'n555',\n", - " 'n556',\n", - " 'n557',\n", - " 'n558',\n", - " 'n559',\n", - " 'n560',\n", - " 'n561',\n", - " 'n562',\n", - " 'n563',\n", - " 'n564',\n", - " 'n565',\n", - " 'n566',\n", - " 'n567',\n", - " 'n568',\n", - " 'n569',\n", - " 'n570',\n", - " 'n571',\n", - " 'n572',\n", - " 'n573',\n", - " 'n574',\n", - " 'n575',\n", - " 'n576',\n", - " 'n577',\n", - " 'n578',\n", - " 'n579',\n", - " 'n580',\n", - " 'n581',\n", - " 'n582',\n", - " 'n583',\n", - " 'n584',\n", - " 'n585',\n", - " 'n586',\n", - " 'n587',\n", - " 'n588',\n", - " 'n589',\n", - " 'n590',\n", - " 'n591',\n", - " 'n592',\n", - " 'n593',\n", - " 'n594',\n", - " 'n595',\n", - " 'n596',\n", - " 'n597',\n", - " 'n598',\n", - " 'n599',\n", - " 'n600',\n", - " 'n601',\n", - " 'n602',\n", - " 'n603',\n", - " 'n604',\n", - " 'n605',\n", - " 'n606',\n", - " 'n607',\n", - " 'n608',\n", - " 'n609',\n", - " 'n610',\n", - " 'n611',\n", - " 'n612',\n", - " 'n613',\n", - " 'n614',\n", - " 'n615',\n", - " 'n616',\n", - " 'n617',\n", - " 'n618',\n", - " 'n619',\n", - " 'n620',\n", - " 'n621',\n", - " 'n622',\n", - " 'n623',\n", - " 'n624',\n", - " 'n625',\n", - " 'n626',\n", - " 'n627',\n", - " 'n628',\n", - " 'n629',\n", - " 'n630',\n", - " 'n631',\n", - " 'n632',\n", - " 'n633',\n", - " 'n634',\n", - " 'n635',\n", - " 'n636',\n", - " 'n637',\n", - " 'n638',\n", - " 'n639',\n", - " 'n640',\n", - " 'n641',\n", - " 'n642',\n", - " 'n643',\n", - " 'n644',\n", - " 'n645',\n", - " 'n646',\n", - " 'n647',\n", - " 'n648',\n", - " 'n649',\n", - " 'n650',\n", - " 'n651',\n", - " 'n652',\n", - " 'n653',\n", - " 'n654',\n", - " 'n655',\n", - " 'n656',\n", - " 'n657',\n", - " 'n658',\n", - " 'n659',\n", - " 'n660',\n", - " 'n661',\n", - " 'n662',\n", - " 'n663',\n", - " 'n664',\n", - " 'n665',\n", - " 'n666',\n", - " 'n667',\n", - " 'n668',\n", - " 'n669',\n", - " 'n670',\n", - " 'n671',\n", - " 'n672',\n", - " 'n673',\n", - " 'n674',\n", - " 'n675',\n", - " 'n676',\n", - " 'n677',\n", - " 'n678',\n", - " 'n679',\n", - " 'n680',\n", - " 'n681',\n", - " 'n682',\n", - " 'n683',\n", - " 'n684',\n", - " 'n685',\n", - " 'n686',\n", - " 'n687',\n", - " 'n688',\n", - " 'n689',\n", - " 'n690',\n", - " 'n691',\n", - " 'n692',\n", - " 'n693',\n", - " 'n694',\n", - " 'n695',\n", - " 'n696',\n", - " 'n697',\n", - " 'n698',\n", - " 'n699',\n", - " 'n700',\n", - " 'n701',\n", - " 'n702',\n", - " 'n703',\n", - " 'n704',\n", - " 'n705',\n", - " 'n706',\n", - " 'n707',\n", - " 'n708',\n", - " 'n709',\n", - " 'n710',\n", - " 'n711',\n", - " 'n712',\n", - " 'n713',\n", - " 'n714',\n", - " 'n715',\n", - " 'n716',\n", - " 'n717',\n", - " 'n718',\n", - " 'n719',\n", - " 'n720',\n", - " 'n721',\n", - " 'n722',\n", - " 'n723',\n", - " 'n724',\n", - " 'n725',\n", - " 'n726',\n", - " 'n727',\n", - " 'n728',\n", - " 'n729',\n", - " 'n730',\n", - " 'n731',\n", - " 'n732',\n", - " 'n733',\n", - " 'n734',\n", - " 'n735',\n", - " 'n736',\n", - " 'n737',\n", - " 'n738',\n", - " 'n739',\n", - " 'n740',\n", - " 'n741',\n", - " 'n742',\n", - " 'n743',\n", - " 'n744',\n", - " 'n745',\n", - " 'n746',\n", - " 'n747',\n", - " 'n748',\n", - " 'n749',\n", - " 'n750',\n", - " 'n751',\n", - " 'n752',\n", - " 'n753',\n", - " 'n754',\n", - " 'n755',\n", - " 'n756',\n", - " 'n757',\n", - " 'n758',\n", - " 'n759',\n", - " 'n760',\n", - " 'n761',\n", - " 'n762',\n", - " 'n763',\n", - " 'n764',\n", - " 'n765',\n", - " 'n766',\n", - " 'n767',\n", - " 'n768',\n", - " 'n769',\n", - " 'n770',\n", - " 'n771',\n", - " 'n772',\n", - " 'n773',\n", - " 'n774',\n", - " 'n775',\n", - " 'n776',\n", - " 'n777',\n", - " 'n778',\n", - " 'n779',\n", - " 'n780',\n", - " 'n781',\n", - " 'n782',\n", - " 'n783',\n", - " 'n784',\n", - " 'n785',\n", - " 'n786',\n", - " 'n787',\n", - " 'n788',\n", - " 'n789',\n", - " 'n790',\n", - " 'n791',\n", - " 'n792',\n", - " 'n793',\n", - " 'n794',\n", - " 'n795',\n", - " 'n796',\n", - " 'n797',\n", - " 'n798',\n", - " 'n799',\n", - " 'n800',\n", - " 'n801',\n", - " 'n802',\n", - " 'n803',\n", - " 'n804',\n", - " 'n805',\n", - " 'n806',\n", - " 'n807',\n", - " 'n808',\n", - " 'n809',\n", - " 'n810',\n", - " 'n811',\n", - " 'n812',\n", - " 'n813',\n", - " 'n814',\n", - " 'n815',\n", - " 'n816',\n", - " 'n817',\n", - " 'n818',\n", - " 'n819',\n", - " 'n820',\n", - " 'n821',\n", - " 'n822',\n", - " 'n823',\n", - " 'n824',\n", - " 'n825',\n", - " 'n826',\n", - " 'n827',\n", - " 'n828',\n", - " 'n829',\n", - " 'n830',\n", - " 'n831',\n", - " 'n832',\n", - " 'n833',\n", - " 'n834',\n", - " 'n835',\n", - " 'n836',\n", - " 'n837',\n", - " 'n838',\n", - " 'n839',\n", - " 'n840',\n", - " 'n841',\n", - " 'n842',\n", - " 'n843',\n", - " 'n844',\n", - " 'n845',\n", - " 'n846',\n", - " 'n847',\n", - " 'n848',\n", - " 'n849',\n", - " 'n850',\n", - " 'n851',\n", - " 'n852',\n", - " 'n853',\n", - " 'n854',\n", - " 'n855',\n", - " 'n856',\n", - " 'n857',\n", - " 'n858',\n", - " 'n859',\n", - " 'n860',\n", - " 'n861',\n", - " 'n862',\n", - " 'n863',\n", - " 'n864',\n", - " 'n865',\n", - " 'n866',\n", - " 'n867',\n", - " 'n868',\n", - " 'n869',\n", - " 'n870',\n", - " 'n871',\n", - " 'n872',\n", - " 'n873',\n", - " 'n874',\n", - " 'n875',\n", - " 'n876',\n", - " 'n877',\n", - " 'n878',\n", - " 'n879',\n", - " 'n880',\n", - " 'n881',\n", - " 'n882',\n", - " 'n883',\n", - " 'n884',\n", - " 'n885',\n", - " 'n886',\n", - " 'n887',\n", - " 'n888',\n", - " 'n889',\n", - " 'n890',\n", - " 'n891',\n", - " 'n892',\n", - " 'n893',\n", - " 'n894',\n", - " 'n895',\n", - " 'n896',\n", - " 'n897',\n", - " 'n898',\n", - " 'n899',\n", - " 'n900',\n", - " 'n901',\n", - " 'n902',\n", - " 'n903',\n", - " 'n904',\n", - " 'n905',\n", - " 'n906',\n", - " 'n907',\n", - " 'n908',\n", - " 'n909',\n", - " 'n910',\n", - " 'n911',\n", - " 'n912',\n", - " 'n913',\n", - " 'n914',\n", - " 'n915',\n", - " 'n916',\n", - " 'n917',\n", - " 'n918',\n", - " 'n919',\n", - " 'n920',\n", - " 'n921',\n", - " 'n922',\n", - " 'n923',\n", - " 'n924',\n", - " 'n925',\n", - " 'n926',\n", - " 'n927',\n", - " 'n928',\n", - " 'n929',\n", - " 'n930',\n", - " 'n931',\n", - " 'n932',\n", - " 'n933',\n", - " 'n934',\n", - " 'n935',\n", - " 'n936',\n", - " 'n937',\n", - " 'n938',\n", - " 'n939',\n", - " 'n940',\n", - " 'n941',\n", - " 'n942',\n", - " 'n943',\n", - " 'n944',\n", - " 'n945',\n", - " 'n946',\n", - " 'n947',\n", - " 'n948',\n", - " 'n949',\n", - " 'n950',\n", - " 'n951',\n", - " 'n952',\n", - " 'n953',\n", - " 'n954',\n", - " 'n955',\n", - " 'n956',\n", - " 'n957',\n", - " 'n958',\n", - " 'n959',\n", - " 'n960',\n", - " 'n961',\n", - " 'n962',\n", - " 'n963',\n", - " 'n964',\n", - " 'n965',\n", - " 'n966',\n", - " 'n967',\n", - " 'n968',\n", - " 'n969',\n", - " 'n970',\n", - " 'n971',\n", - " 'n972',\n", - " 'n973',\n", - " 'n974',\n", - " 'n975',\n", - " 'n976',\n", - " 'n977',\n", - " 'n978',\n", - " 'n979',\n", - " 'n980',\n", - " 'n981',\n", - " 'n982',\n", - " 'n983',\n", - " 'n984',\n", - " 'n985',\n", - " 'n986',\n", - " 'n987',\n", - " 'n988',\n", - " 'n989',\n", - " 'n990',\n", - " 'n991',\n", - " 'n992',\n", - " 'n993',\n", - " 'n994',\n", - " 'n995',\n", - " 'n996',\n", - " 'n997',\n", - " 'n998',\n", - " 'n999',\n", - " ...]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "a2_names" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1341,18 +236,9 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Feature columns do not sum to 1.0 for all samples - so they are being transformed.\n", - "Train: (3170, 5654), Test: (779, 5654)\n" - ] - } - ], + "outputs": [], "source": [ "# load metadata\n", "target = \"age_months\"\n", @@ -1368,7 +254,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1385,7 +271,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1401,22 +287,9 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([' g__Fusobacterium', ' g__Rheinheimera',\n", - " ' s__uncultured_bacterium', ..., 'n5575', 'n5576', 'n5577'],\n", - " dtype=' for now it's just n + count\n", "label = df_taxonomy_f[\"Taxon\"].values\n", - "label_short = np.array([la.split(\";\")[-1] for la in label])\n", + "label_short = np.array([la.split(\";\")[-1].strip() for la in label])\n", "assert len(label) == len(ft_cols)\n", "assert len(label) == len(label_short)\n", "label = np.append(label, a2_names)\n", @@ -1436,31 +309,9 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " \n", - " \n", - "FORMULATION: R1\n", - " \n", - "MODEL SELECTION COMPUTED: \n", - " Cross Validation\n", - " \n", - "CROSS VALIDATION PARAMETERS: \n", - " numerical_method : not specified\n", - " one-SE method : True\n", - " Nsubset = 5\n", - " lamin = 0.001\n", - " Nlam = 80\n", - " with log-scale\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "# perform CV classo: trac\n", "problem = classo_problem(log_geom_trainval, y_train_val.values, label=label_short)\n", @@ -1496,34 +347,116 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[37], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mproblem\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msolve\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(problem\u001b[38;5;241m.\u001b[39msolution)\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/solver.py:153\u001b[0m, in \u001b[0;36mclasso_problem.solve\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 151\u001b[0m \u001b[38;5;66;03m# Compute the cross validation thanks to the class solution_CV which contains directely the computation in the initialisation\u001b[39;00m\n\u001b[1;32m 152\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmodel_selection\u001b[38;5;241m.\u001b[39mCV:\n\u001b[0;32m--> 153\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msolution\u001b[38;5;241m.\u001b[39mCV \u001b[38;5;241m=\u001b[39m \u001b[43msolution_CV\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 154\u001b[0m \u001b[43m \u001b[49m\u001b[43mmatrices\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 155\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel_selection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCVparameters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 156\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformulation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 157\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnumerical_method\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 158\u001b[0m \u001b[43m \u001b[49m\u001b[43mlabel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 159\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 161\u001b[0m \u001b[38;5;66;03m# Compute the Stability Selection thanks to the class solution_SS which contains directely the computation in the initialisation\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmodel_selection\u001b[38;5;241m.\u001b[39mStabSel:\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/solver.py:985\u001b[0m, in \u001b[0;36msolution_CV.__init__\u001b[0;34m(self, matrices, param, formulation, numerical_method, label)\u001b[0m\n\u001b[1;32m 982\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlogscale \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mlogscale\n\u001b[1;32m 984\u001b[0m \u001b[38;5;66;03m# Compute the solution and is the formulation is concomitant, it also compute sigma\u001b[39;00m\n\u001b[0;32m--> 985\u001b[0m (out, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39myGraph, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstandard_error, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindex_min, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindex_1SE,) \u001b[38;5;241m=\u001b[39m \u001b[43mCV\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mmatrices\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mNsubset\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mtyp\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mname_formulation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mnum_meth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnumerical_method\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43mlambdas\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlambdas\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 991\u001b[0m \u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mseed\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 992\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 993\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho_classification\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho_classification\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 994\u001b[0m \u001b[43m \u001b[49m\u001b[43moneSE\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moneSE\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 995\u001b[0m \u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 996\u001b[0m \u001b[43m \u001b[49m\u001b[43mw\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformulation\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 997\u001b[0m \u001b[43m \u001b[49m\u001b[43mintercept\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparam\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformulation\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mintercept\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 998\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1000\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mxGraph \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mlambdas\n\u001b[1;32m 1001\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlambda_1SE \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mlambdas[\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindex_1SE]\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/cross_validation.py:176\u001b[0m, in \u001b[0;36mCV\u001b[0;34m(matrices, k, typ, num_meth, seed, rho, rho_classification, e, lambdas, Nlam, oneSE, w, intercept)\u001b[0m\n\u001b[1;32m 174\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 175\u001b[0m lam \u001b[38;5;241m=\u001b[39m lambdas[i]\n\u001b[0;32m--> 176\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[43mClasso\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 177\u001b[0m \u001b[43m \u001b[49m\u001b[43mmatrices\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 178\u001b[0m \u001b[43m \u001b[49m\u001b[43mlam\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 179\u001b[0m \u001b[43m \u001b[49m\u001b[43mtyp\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtyp\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 180\u001b[0m \u001b[43m \u001b[49m\u001b[43mmeth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnum_meth\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 181\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 182\u001b[0m \u001b[43m \u001b[49m\u001b[43me\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 183\u001b[0m \u001b[43m \u001b[49m\u001b[43mrho_classification\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrho_classification\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 184\u001b[0m \u001b[43m \u001b[49m\u001b[43mw\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 185\u001b[0m \u001b[43m \u001b[49m\u001b[43mintercept\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mintercept\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 186\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 187\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m (out, MSE, SE, i, i_1SE)\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/compact_func.py:163\u001b[0m, in \u001b[0;36mClasso\u001b[0;34m(matrix, lam, typ, meth, rho, get_lambdamax, true_lam, e, rho_classification, w, intercept, return_sigm)\u001b[0m\n\u001b[1;32m 161\u001b[0m beta \u001b[38;5;241m=\u001b[39m Classo_R1(pb, lam \u001b[38;5;241m/\u001b[39m lambdamax)\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 163\u001b[0m beta \u001b[38;5;241m=\u001b[39m \u001b[43mClasso_R1\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlam\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 165\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m intercept:\n\u001b[1;32m 166\u001b[0m betaO \u001b[38;5;241m=\u001b[39m ybar \u001b[38;5;241m-\u001b[39m np\u001b[38;5;241m.\u001b[39mvdot(Xbar, beta)\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/solve_R1.py:28\u001b[0m, in \u001b[0;36mClasso_R1\u001b[0;34m(pb, lam)\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[38;5;66;03m# ODE\u001b[39;00m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;66;03m# here we compute the path algo until our lambda, and just take the last beta\u001b[39;00m\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m pb_type \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPath-Alg\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m---> 28\u001b[0m BETA \u001b[38;5;241m=\u001b[39m \u001b[43msolve_path\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpb\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmatrix\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlam\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mR1\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m 29\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m BETA[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]\n\u001b[1;32m 31\u001b[0m regpath \u001b[38;5;241m=\u001b[39m pb\u001b[38;5;241m.\u001b[39mregpath\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:169\u001b[0m, in \u001b[0;36msolve_path\u001b[0;34m(matrices, lamin, n_active, rho, typ, intercept)\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m BETA, LAM\n\u001b[1;32m 161\u001b[0m \u001b[38;5;66;03m# elif not np.any(param.F):\u001b[39;00m\n\u001b[1;32m 162\u001b[0m \u001b[38;5;66;03m# print(param.r)\u001b[39;00m\n\u001b[1;32m 163\u001b[0m \u001b[38;5;66;03m# raise ValueError(\"The problem looks infeasible because the set of active sample became zero, \"\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 166\u001b[0m \u001b[38;5;66;03m# \"with intercept ? {} \"\u001b[39;00m\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# \"with rho equal to {} \".format(i, typ, intercept, rho ))\u001b[39;00m\n\u001b[0;32m--> 169\u001b[0m \u001b[43mup\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparam\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 170\u001b[0m BETA\u001b[38;5;241m.\u001b[39mappend(param\u001b[38;5;241m.\u001b[39mbeta), LAM\u001b[38;5;241m.\u001b[39mappend(param\u001b[38;5;241m.\u001b[39mlam)\n\u001b[1;32m 172\u001b[0m \u001b[38;5;66;03m# print(\"inside : \", param.r[ param.F], np.nonzero(param.F)[0] )\u001b[39;00m\n\u001b[1;32m 173\u001b[0m \u001b[38;5;66;03m# print(\" outside : \", param.r[~param.F], np.nonzero(~param.F)[0])\u001b[39;00m\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:291\u001b[0m, in \u001b[0;36mup\u001b[0;34m(param)\u001b[0m\n\u001b[1;32m 289\u001b[0m formulation \u001b[38;5;241m=\u001b[39m param\u001b[38;5;241m.\u001b[39mformulation\n\u001b[1;32m 290\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m formulation \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mR1\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mR3\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[0;32m--> 291\u001b[0m \u001b[43mup_LS\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparam\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 292\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m formulation \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mR2\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 293\u001b[0m up_huber(param)\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:327\u001b[0m, in \u001b[0;36mup_LS\u001b[0;34m(param)\u001b[0m\n\u001b[1;32m 325\u001b[0m L \u001b[38;5;241m=\u001b[39m [lam] \u001b[38;5;241m*\u001b[39m d\n\u001b[1;32m 326\u001b[0m Mat \u001b[38;5;241m=\u001b[39m M[:d, :d]\n\u001b[0;32m--> 327\u001b[0m beta_dot, lam_s_dot \u001b[38;5;241m=\u001b[39m \u001b[43mderivatives\u001b[49m\u001b[43m(\u001b[49m\u001b[43mactivity\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43ms\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mMat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mM\u001b[49m\u001b[43m[\u001b[49m\u001b[43md\u001b[49m\u001b[43m:\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m:\u001b[49m\u001b[43md\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mXt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43midr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumber_act\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 328\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(d):\n\u001b[1;32m 329\u001b[0m bi, di, e, s0 \u001b[38;5;241m=\u001b[39m beta[i], beta_dot[i], lam_s_dot[i], s[i]\n", - "File \u001b[0;32m~/miniforge3/envs/ritme_wclasso/lib/python3.8/site-packages/classo/path_alg.py:784\u001b[0m, in \u001b[0;36mderivatives\u001b[0;34m(activity, s, Mat, C, Inv, idr, number_act)\u001b[0m\n\u001b[1;32m 782\u001b[0m beta_dot \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mzeros(\u001b[38;5;28mlen\u001b[39m(activity))\n\u001b[1;32m 783\u001b[0m beta_dot[activity] \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m-\u001b[39mInv[:number_act, :number_act]\u001b[38;5;241m.\u001b[39mdot(s[activity])\n\u001b[0;32m--> 784\u001b[0m lam_s_dot \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m-\u001b[39m\u001b[43mMat\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mbeta_dot\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 786\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(C) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 787\u001b[0m v_dot \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mzeros(\u001b[38;5;28mlen\u001b[39m(C))\n", - "\u001b[0;31mKeyboardInterrupt\u001b[0m: " - ] - } - ], + "outputs": [], "source": [ "problem.solve()\n", + "# todo: find out how to extract the insights from the model to disk without changing classo\n", "print(problem.solution)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# alpha [0] is learned intercept, alpha [1:] are learned coefficients for all features\n", + "# in logGeom (n_samples, n_features)\n", + "# ! if oneSE=True -> uses lambda_1SE else lambda_min (see CV in\n", + "# ! classo>cross_validation.py)\n", + "# refit -> solves unconstrained least squares problem with selected lambda and\n", + "# variables\n", + "alpha = problem.solution.CV.refit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ! class solution_CV: defined in @solver.py L930\n", + "selection = problem.solution.CV.selected_param[1:] # exclude the intercept\n", + "selected_ft = label[selection]\n", + "print(selected_ft)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # selected lambda with 1-standard-error method\n", + "# problem.solution.CV.lambda_1SE\n", + "\n", + "# # selected lambda without 1-standard-error method\n", + "# problem.solution.CV.lambda_min" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# save model: A, label, alpha (includes selected_ft)\n", + "path2out = \"test_model\"\n", + "if not os.path.exists(path2out):\n", + " os.makedirs(path2out)\n", + "\n", + "# storing A w labels\n", + "df_A_with_labels = pd.DataFrame(A, columns=label, index=label[:nb_features])\n", + "df_A_with_labels.to_csv(os.path.join(path2out, \"matrix_a_w_labels.csv\"), index=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# storing alpha w labels\n", + "idx_alpha = [\"intercept\"] + label.tolist()\n", + "df_alpha_with_labels = pd.DataFrame(alpha, columns=[\"alpha\"], index=idx_alpha)\n", + "df_alpha_with_labels.to_csv(\n", + " os.path.join(path2out, \"model_alpha_w_labels.csv\"), index=True\n", + ")\n", + "\n", + "# we can get selected features from alpha\n", + "selected_ft_inf = df_alpha_with_labels[\n", + " df_alpha_with_labels[\"alpha\"] != 0\n", + "].index.tolist()\n", + "assert selected_ft_inf[1:] == selected_ft.tolist()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Perform prediction on test set" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# derive log_geom for test\n", + "ft_cols = [x for x in test.columns if x.startswith(\"F\")]\n", + "\n", + "x_test = test[ft_cols]\n", + "y_test = test[target]\n", + "# todo: read A\n", + "log_geom_test, nleaves = _preprocess_taxonomy_aggregation(x_test.values, A)\n", + "\n", + "# apply model to test\n", + "# todo: read alpha\n", + "y_test_pred = log_geom_test.dot(alpha[1:]) + alpha[0]" + ] } ], "metadata": { diff --git a/experiments/test_classo.ipynb b/experiments/test_classo.ipynb index 5bc6922..b836e09 100644 --- a/experiments/test_classo.ipynb +++ b/experiments/test_classo.ipynb @@ -59,33 +59,54 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "array([[ 0, 0, 1, ..., 0, 0, 0],\n", - " [ 0, 4, 0, ..., 16, 31, 0],\n", - " [ 0, 0, 7, ..., 0, 0, 0],\n", + "array(['Life::k__Bacteria::p__Proteobacteria::c__Gammaproteobacteria::o__Legionellales::f__Coxiellaceae::g__Aquicella::s__1::OTU_2211',\n", + " 'Life::k__Bacteria::p__Proteobacteria::c__Gammaproteobacteria::o__Legionellales::f__Coxiellaceae::g__Aquicella::s__2::OTU_1172',\n", + " 'Life::k__Bacteria::p__Proteobacteria::c__Gammaproteobacteria::o__Legionellales::f__Coxiellaceae::g__Aquicella::s__3::OTU_1734',\n", " ...,\n", - " [ 0, 0, 2, ..., 0, 0, 2],\n", - " [ 4, 0, 0, ..., 8, 4, 0],\n", - " [ 1, 7, 0, ..., 1, 6, 0]], dtype=int32)" + " 'Life::k__Bacteria::p__Proteobacteria::c__Gammaproteobacteria',\n", + " 'Life::k__Bacteria::p__Proteobacteria', 'Life::k__Bacteria'],\n", + " dtype=' Date: Fri, 3 May 2024 17:28:52 +0200 Subject: [PATCH 05/28] wip to setup trac in ritme --- experiments/implement_matrixA.ipynb | 1282 +++++++++++++++++++- q2_ritme/model_space/_static_trainables.py | 27 +- q2_ritme/process_data.py | 62 +- q2_ritme/run_config.json | 22 +- q2_ritme/run_n_eval_tune.py | 7 +- q2_ritme/tune_models.py | 9 + 6 files changed, 1346 insertions(+), 63 deletions(-) diff --git a/experiments/implement_matrixA.ipynb b/experiments/implement_matrixA.ipynb index 1ed0afb..99f05ba 100644 --- a/experiments/implement_matrixA.ipynb +++ b/experiments/implement_matrixA.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -75,9 +75,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " /-f1\n", + " /n1------|\n", + "-n2------| \\-f2\n", + " |\n", + " \\-f3\n" + ] + } + ], "source": [ "# Create the tree nodes with lengths\n", "n1 = TreeNode(name=\"n1\")\n", @@ -99,9 +111,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 0., 0., 1.],\n", + " [0., 1., 0., 1.],\n", + " [0., 0., 1., 0.]])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "A_example, a2_names_ex = create_matrix_from_tree(tree)\n", "A_example" @@ -109,9 +134,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "['n0']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a2_names_ex" ] @@ -125,9 +161,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(9478, 5580)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# read feature table\n", "art_feature_table = q2.Artifact.load(\"data/220728_monthly/all_otu_table_filt.qza\")\n", @@ -137,10 +184,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(5608, 2)\n", + "(5580, 2)\n" + ] + } + ], "source": [ + "# read taxonomy\n", "path_to_taxonomy = \"data/220728_monthly/otu_taxonomy_all.qza\"\n", "art_taxonomy = q2.Artifact.load(path_to_taxonomy)\n", "df_taxonomy = art_taxonomy.view(pd.DataFrame)\n", @@ -153,9 +210,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "870198" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# read silva phylo tree\n", "path_to_phylo = \"data/220728_monthly/silva-138-99-rooted-tree.qza\"\n", @@ -167,9 +235,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "11159" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# filter tree by feature table: this prunes a phylogenetic tree to match the\n", "# input ids\n", @@ -182,7 +261,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -193,9 +272,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 0., 0., ..., 0., 0., 0.],\n", + " [0., 1., 0., ..., 0., 0., 0.],\n", + " [0., 0., 1., ..., 0., 0., 0.],\n", + " ...,\n", + " [0., 0., 0., ..., 0., 1., 1.],\n", + " [0., 0., 0., ..., 1., 1., 1.],\n", + " [0., 0., 0., ..., 1., 1., 1.]])" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "A, a2_names = create_matrix_from_tree(tree_phylo_f)\n", "A" @@ -203,16 +299,1027 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "['n0',\n", + " 'n1',\n", + " 'n2',\n", + " 'n3',\n", + " 'n4',\n", + " 'n5',\n", + " 'n6',\n", + " 'n7',\n", + " 'n8',\n", + " 'n9',\n", + " 'n10',\n", + " 'n11',\n", + " 'n12',\n", + " 'n13',\n", + " 'n14',\n", + " 'n15',\n", + " 'n16',\n", + " 'n17',\n", + " 'n18',\n", + " 'n19',\n", + " 'n20',\n", + " 'n21',\n", + " 'n22',\n", + " 'n23',\n", + " 'n24',\n", + " 'n25',\n", + " 'n26',\n", + " 'n27',\n", + " 'n28',\n", + " 'n29',\n", + " 'n30',\n", + " 'n31',\n", + " 'n32',\n", + " 'n33',\n", + " 'n34',\n", + " 'n35',\n", + " 'n36',\n", + " 'n37',\n", + " 'n38',\n", + " 'n39',\n", + " 'n40',\n", + " 'n41',\n", + " 'n42',\n", + " 'n43',\n", + " 'n44',\n", + " 'n45',\n", + " 'n46',\n", + " 'n47',\n", + " 'n48',\n", + " 'n49',\n", + " 'n50',\n", + " 'n51',\n", + " 'n52',\n", + " 'n53',\n", + " 'n54',\n", + " 'n55',\n", + " 'n56',\n", + " 'n57',\n", + " 'n58',\n", + " 'n59',\n", + " 'n60',\n", + " 'n61',\n", + " 'n62',\n", + " 'n63',\n", + " 'n64',\n", + " 'n65',\n", + " 'n66',\n", + " 'n67',\n", + " 'n68',\n", + " 'n69',\n", + " 'n70',\n", + " 'n71',\n", + " 'n72',\n", + " 'n73',\n", + " 'n74',\n", + " 'n75',\n", + " 'n76',\n", + " 'n77',\n", + " 'n78',\n", + " 'n79',\n", + " 'n80',\n", + " 'n81',\n", + " 'n82',\n", + " 'n83',\n", + " 'n84',\n", + " 'n85',\n", + " 'n86',\n", + " 'n87',\n", + " 'n88',\n", + " 'n89',\n", + " 'n90',\n", + " 'n91',\n", + " 'n92',\n", + " 'n93',\n", + " 'n94',\n", + " 'n95',\n", + " 'n96',\n", + " 'n97',\n", + " 'n98',\n", + " 'n99',\n", + " 'n100',\n", + " 'n101',\n", + " 'n102',\n", + " 'n103',\n", + " 'n104',\n", + " 'n105',\n", + " 'n106',\n", + " 'n107',\n", + " 'n108',\n", + " 'n109',\n", + " 'n110',\n", + " 'n111',\n", + " 'n112',\n", + " 'n113',\n", + " 'n114',\n", + " 'n115',\n", + " 'n116',\n", + " 'n117',\n", + " 'n118',\n", + " 'n119',\n", + " 'n120',\n", + " 'n121',\n", + " 'n122',\n", + " 'n123',\n", + " 'n124',\n", + " 'n125',\n", + " 'n126',\n", + " 'n127',\n", + " 'n128',\n", + " 'n129',\n", + " 'n130',\n", + " 'n131',\n", + " 'n132',\n", + " 'n133',\n", + " 'n134',\n", + " 'n135',\n", + " 'n136',\n", + " 'n137',\n", + " 'n138',\n", + " 'n139',\n", + " 'n140',\n", + " 'n141',\n", + " 'n142',\n", + " 'n143',\n", + " 'n144',\n", + " 'n145',\n", + " 'n146',\n", + " 'n147',\n", + " 'n148',\n", + " 'n149',\n", + " 'n150',\n", + " 'n151',\n", + " 'n152',\n", + " 'n153',\n", + " 'n154',\n", + " 'n155',\n", + " 'n156',\n", + " 'n157',\n", + " 'n158',\n", + " 'n159',\n", + " 'n160',\n", + " 'n161',\n", + " 'n162',\n", + " 'n163',\n", + " 'n164',\n", + " 'n165',\n", + " 'n166',\n", + " 'n167',\n", + " 'n168',\n", + " 'n169',\n", + " 'n170',\n", + " 'n171',\n", + " 'n172',\n", + " 'n173',\n", + " 'n174',\n", + " 'n175',\n", + " 'n176',\n", + " 'n177',\n", + " 'n178',\n", + " 'n179',\n", + " 'n180',\n", + " 'n181',\n", + " 'n182',\n", + " 'n183',\n", + " 'n184',\n", + " 'n185',\n", + " 'n186',\n", + " 'n187',\n", + " 'n188',\n", + " 'n189',\n", + " 'n190',\n", + " 'n191',\n", + " 'n192',\n", + " 'n193',\n", + " 'n194',\n", + " 'n195',\n", + " 'n196',\n", + " 'n197',\n", + " 'n198',\n", + " 'n199',\n", + " 'n200',\n", + " 'n201',\n", + " 'n202',\n", + " 'n203',\n", + " 'n204',\n", + " 'n205',\n", + " 'n206',\n", + " 'n207',\n", + " 'n208',\n", + " 'n209',\n", + " 'n210',\n", + " 'n211',\n", + " 'n212',\n", + " 'n213',\n", + " 'n214',\n", + " 'n215',\n", + " 'n216',\n", + " 'n217',\n", + " 'n218',\n", + " 'n219',\n", + " 'n220',\n", + " 'n221',\n", + " 'n222',\n", + " 'n223',\n", + " 'n224',\n", + " 'n225',\n", + " 'n226',\n", + " 'n227',\n", + " 'n228',\n", + " 'n229',\n", + " 'n230',\n", + " 'n231',\n", + " 'n232',\n", + " 'n233',\n", + " 'n234',\n", + " 'n235',\n", + " 'n236',\n", + " 'n237',\n", + " 'n238',\n", + " 'n239',\n", + " 'n240',\n", + " 'n241',\n", + " 'n242',\n", + " 'n243',\n", + " 'n244',\n", + " 'n245',\n", + " 'n246',\n", + " 'n247',\n", + " 'n248',\n", + " 'n249',\n", + " 'n250',\n", + " 'n251',\n", + " 'n252',\n", + " 'n253',\n", + " 'n254',\n", + " 'n255',\n", + " 'n256',\n", + " 'n257',\n", + " 'n258',\n", + " 'n259',\n", + " 'n260',\n", + " 'n261',\n", + " 'n262',\n", + " 'n263',\n", + " 'n264',\n", + " 'n265',\n", + " 'n266',\n", + " 'n267',\n", + " 'n268',\n", + " 'n269',\n", + " 'n270',\n", + " 'n271',\n", + " 'n272',\n", + " 'n273',\n", + " 'n274',\n", + " 'n275',\n", + " 'n276',\n", + " 'n277',\n", + " 'n278',\n", + " 'n279',\n", + " 'n280',\n", + " 'n281',\n", + " 'n282',\n", + " 'n283',\n", + " 'n284',\n", + " 'n285',\n", + " 'n286',\n", + " 'n287',\n", + " 'n288',\n", + " 'n289',\n", + " 'n290',\n", + " 'n291',\n", + " 'n292',\n", + " 'n293',\n", + " 'n294',\n", + " 'n295',\n", + " 'n296',\n", + " 'n297',\n", + " 'n298',\n", + " 'n299',\n", + " 'n300',\n", + " 'n301',\n", + " 'n302',\n", + " 'n303',\n", + " 'n304',\n", + " 'n305',\n", + " 'n306',\n", + " 'n307',\n", + " 'n308',\n", + " 'n309',\n", + " 'n310',\n", + " 'n311',\n", + " 'n312',\n", + " 'n313',\n", + " 'n314',\n", + " 'n315',\n", + " 'n316',\n", + " 'n317',\n", + " 'n318',\n", + " 'n319',\n", + " 'n320',\n", + " 'n321',\n", + " 'n322',\n", + " 'n323',\n", + " 'n324',\n", + " 'n325',\n", + " 'n326',\n", + " 'n327',\n", + " 'n328',\n", + " 'n329',\n", + " 'n330',\n", + " 'n331',\n", + " 'n332',\n", + " 'n333',\n", + " 'n334',\n", + " 'n335',\n", + " 'n336',\n", + " 'n337',\n", + " 'n338',\n", + " 'n339',\n", + " 'n340',\n", + " 'n341',\n", + " 'n342',\n", + " 'n343',\n", + " 'n344',\n", + " 'n345',\n", + " 'n346',\n", + " 'n347',\n", + " 'n348',\n", + " 'n349',\n", + " 'n350',\n", + " 'n351',\n", + " 'n352',\n", + " 'n353',\n", + " 'n354',\n", + " 'n355',\n", + " 'n356',\n", + " 'n357',\n", + " 'n358',\n", + " 'n359',\n", + " 'n360',\n", + " 'n361',\n", + " 'n362',\n", + " 'n363',\n", + " 'n364',\n", + " 'n365',\n", + " 'n366',\n", + " 'n367',\n", + " 'n368',\n", + " 'n369',\n", + " 'n370',\n", + " 'n371',\n", + " 'n372',\n", + " 'n373',\n", + " 'n374',\n", + " 'n375',\n", + " 'n376',\n", + " 'n377',\n", + " 'n378',\n", + " 'n379',\n", + " 'n380',\n", + " 'n381',\n", + " 'n382',\n", + " 'n383',\n", + " 'n384',\n", + " 'n385',\n", + " 'n386',\n", + " 'n387',\n", + " 'n388',\n", + " 'n389',\n", + " 'n390',\n", + " 'n391',\n", + " 'n392',\n", + " 'n393',\n", + " 'n394',\n", + " 'n395',\n", + " 'n396',\n", + " 'n397',\n", + " 'n398',\n", + " 'n399',\n", + " 'n400',\n", + " 'n401',\n", + " 'n402',\n", + " 'n403',\n", + " 'n404',\n", + " 'n405',\n", + " 'n406',\n", + " 'n407',\n", + " 'n408',\n", + " 'n409',\n", + " 'n410',\n", + " 'n411',\n", + " 'n412',\n", + " 'n413',\n", + " 'n414',\n", + " 'n415',\n", + " 'n416',\n", + " 'n417',\n", + " 'n418',\n", + " 'n419',\n", + " 'n420',\n", + " 'n421',\n", + " 'n422',\n", + " 'n423',\n", + " 'n424',\n", + " 'n425',\n", + " 'n426',\n", + " 'n427',\n", + " 'n428',\n", + " 'n429',\n", + " 'n430',\n", + " 'n431',\n", + " 'n432',\n", + " 'n433',\n", + " 'n434',\n", + " 'n435',\n", + " 'n436',\n", + " 'n437',\n", + " 'n438',\n", + " 'n439',\n", + " 'n440',\n", + " 'n441',\n", + " 'n442',\n", + " 'n443',\n", + " 'n444',\n", + " 'n445',\n", + " 'n446',\n", + " 'n447',\n", + " 'n448',\n", + " 'n449',\n", + " 'n450',\n", + " 'n451',\n", + " 'n452',\n", + " 'n453',\n", + " 'n454',\n", + " 'n455',\n", + " 'n456',\n", + " 'n457',\n", + " 'n458',\n", + " 'n459',\n", + " 'n460',\n", + " 'n461',\n", + " 'n462',\n", + " 'n463',\n", + " 'n464',\n", + " 'n465',\n", + " 'n466',\n", + " 'n467',\n", + " 'n468',\n", + " 'n469',\n", + " 'n470',\n", + " 'n471',\n", + " 'n472',\n", + " 'n473',\n", + " 'n474',\n", + " 'n475',\n", + " 'n476',\n", + " 'n477',\n", + " 'n478',\n", + " 'n479',\n", + " 'n480',\n", + " 'n481',\n", + " 'n482',\n", + " 'n483',\n", + " 'n484',\n", + " 'n485',\n", + " 'n486',\n", + " 'n487',\n", + " 'n488',\n", + " 'n489',\n", + " 'n490',\n", + " 'n491',\n", + " 'n492',\n", + " 'n493',\n", + " 'n494',\n", + " 'n495',\n", + " 'n496',\n", + " 'n497',\n", + " 'n498',\n", + " 'n499',\n", + " 'n500',\n", + " 'n501',\n", + " 'n502',\n", + " 'n503',\n", + " 'n504',\n", + " 'n505',\n", + " 'n506',\n", + " 'n507',\n", + " 'n508',\n", + " 'n509',\n", + " 'n510',\n", + " 'n511',\n", + " 'n512',\n", + " 'n513',\n", + " 'n514',\n", + " 'n515',\n", + " 'n516',\n", + " 'n517',\n", + " 'n518',\n", + " 'n519',\n", + " 'n520',\n", + " 'n521',\n", + " 'n522',\n", + " 'n523',\n", + " 'n524',\n", + " 'n525',\n", + " 'n526',\n", + " 'n527',\n", + " 'n528',\n", + " 'n529',\n", + " 'n530',\n", + " 'n531',\n", + " 'n532',\n", + " 'n533',\n", + " 'n534',\n", + " 'n535',\n", + " 'n536',\n", + " 'n537',\n", + " 'n538',\n", + " 'n539',\n", + " 'n540',\n", + " 'n541',\n", + " 'n542',\n", + " 'n543',\n", + " 'n544',\n", + " 'n545',\n", + " 'n546',\n", + " 'n547',\n", + " 'n548',\n", + " 'n549',\n", + " 'n550',\n", + " 'n551',\n", + " 'n552',\n", + " 'n553',\n", + " 'n554',\n", + " 'n555',\n", + " 'n556',\n", + " 'n557',\n", + " 'n558',\n", + " 'n559',\n", + " 'n560',\n", + " 'n561',\n", + " 'n562',\n", + " 'n563',\n", + " 'n564',\n", + " 'n565',\n", + " 'n566',\n", + " 'n567',\n", + " 'n568',\n", + " 'n569',\n", + " 'n570',\n", + " 'n571',\n", + " 'n572',\n", + " 'n573',\n", + " 'n574',\n", + " 'n575',\n", + " 'n576',\n", + " 'n577',\n", + " 'n578',\n", + " 'n579',\n", + " 'n580',\n", + " 'n581',\n", + " 'n582',\n", + " 'n583',\n", + " 'n584',\n", + " 'n585',\n", + " 'n586',\n", + " 'n587',\n", + " 'n588',\n", + " 'n589',\n", + " 'n590',\n", + " 'n591',\n", + " 'n592',\n", + " 'n593',\n", + " 'n594',\n", + " 'n595',\n", + " 'n596',\n", + " 'n597',\n", + " 'n598',\n", + " 'n599',\n", + " 'n600',\n", + " 'n601',\n", + " 'n602',\n", + " 'n603',\n", + " 'n604',\n", + " 'n605',\n", + " 'n606',\n", + " 'n607',\n", + " 'n608',\n", + " 'n609',\n", + " 'n610',\n", + " 'n611',\n", + " 'n612',\n", + " 'n613',\n", + " 'n614',\n", + " 'n615',\n", + " 'n616',\n", + " 'n617',\n", + " 'n618',\n", + " 'n619',\n", + " 'n620',\n", + " 'n621',\n", + " 'n622',\n", + " 'n623',\n", + " 'n624',\n", + " 'n625',\n", + " 'n626',\n", + " 'n627',\n", + " 'n628',\n", + " 'n629',\n", + " 'n630',\n", + " 'n631',\n", + " 'n632',\n", + " 'n633',\n", + " 'n634',\n", + " 'n635',\n", + " 'n636',\n", + " 'n637',\n", + " 'n638',\n", + " 'n639',\n", + " 'n640',\n", + " 'n641',\n", + " 'n642',\n", + " 'n643',\n", + " 'n644',\n", + " 'n645',\n", + " 'n646',\n", + " 'n647',\n", + " 'n648',\n", + " 'n649',\n", + " 'n650',\n", + " 'n651',\n", + " 'n652',\n", + " 'n653',\n", + " 'n654',\n", + " 'n655',\n", + " 'n656',\n", + " 'n657',\n", + " 'n658',\n", + " 'n659',\n", + " 'n660',\n", + " 'n661',\n", + " 'n662',\n", + " 'n663',\n", + " 'n664',\n", + " 'n665',\n", + " 'n666',\n", + " 'n667',\n", + " 'n668',\n", + " 'n669',\n", + " 'n670',\n", + " 'n671',\n", + " 'n672',\n", + " 'n673',\n", + " 'n674',\n", + " 'n675',\n", + " 'n676',\n", + " 'n677',\n", + " 'n678',\n", + " 'n679',\n", + " 'n680',\n", + " 'n681',\n", + " 'n682',\n", + " 'n683',\n", + " 'n684',\n", + " 'n685',\n", + " 'n686',\n", + " 'n687',\n", + " 'n688',\n", + " 'n689',\n", + " 'n690',\n", + " 'n691',\n", + " 'n692',\n", + " 'n693',\n", + " 'n694',\n", + " 'n695',\n", + " 'n696',\n", + " 'n697',\n", + " 'n698',\n", + " 'n699',\n", + " 'n700',\n", + " 'n701',\n", + " 'n702',\n", + " 'n703',\n", + " 'n704',\n", + " 'n705',\n", + " 'n706',\n", + " 'n707',\n", + " 'n708',\n", + " 'n709',\n", + " 'n710',\n", + " 'n711',\n", + " 'n712',\n", + " 'n713',\n", + " 'n714',\n", + " 'n715',\n", + " 'n716',\n", + " 'n717',\n", + " 'n718',\n", + " 'n719',\n", + " 'n720',\n", + " 'n721',\n", + " 'n722',\n", + " 'n723',\n", + " 'n724',\n", + " 'n725',\n", + " 'n726',\n", + " 'n727',\n", + " 'n728',\n", + " 'n729',\n", + " 'n730',\n", + " 'n731',\n", + " 'n732',\n", + " 'n733',\n", + " 'n734',\n", + " 'n735',\n", + " 'n736',\n", + " 'n737',\n", + " 'n738',\n", + " 'n739',\n", + " 'n740',\n", + " 'n741',\n", + " 'n742',\n", + " 'n743',\n", + " 'n744',\n", + " 'n745',\n", + " 'n746',\n", + " 'n747',\n", + " 'n748',\n", + " 'n749',\n", + " 'n750',\n", + " 'n751',\n", + " 'n752',\n", + " 'n753',\n", + " 'n754',\n", + " 'n755',\n", + " 'n756',\n", + " 'n757',\n", + " 'n758',\n", + " 'n759',\n", + " 'n760',\n", + " 'n761',\n", + " 'n762',\n", + " 'n763',\n", + " 'n764',\n", + " 'n765',\n", + " 'n766',\n", + " 'n767',\n", + " 'n768',\n", + " 'n769',\n", + " 'n770',\n", + " 'n771',\n", + " 'n772',\n", + " 'n773',\n", + " 'n774',\n", + " 'n775',\n", + " 'n776',\n", + " 'n777',\n", + " 'n778',\n", + " 'n779',\n", + " 'n780',\n", + " 'n781',\n", + " 'n782',\n", + " 'n783',\n", + " 'n784',\n", + " 'n785',\n", + " 'n786',\n", + " 'n787',\n", + " 'n788',\n", + " 'n789',\n", + " 'n790',\n", + " 'n791',\n", + " 'n792',\n", + " 'n793',\n", + " 'n794',\n", + " 'n795',\n", + " 'n796',\n", + " 'n797',\n", + " 'n798',\n", + " 'n799',\n", + " 'n800',\n", + " 'n801',\n", + " 'n802',\n", + " 'n803',\n", + " 'n804',\n", + " 'n805',\n", + " 'n806',\n", + " 'n807',\n", + " 'n808',\n", + " 'n809',\n", + " 'n810',\n", + " 'n811',\n", + " 'n812',\n", + " 'n813',\n", + " 'n814',\n", + " 'n815',\n", + " 'n816',\n", + " 'n817',\n", + " 'n818',\n", + " 'n819',\n", + " 'n820',\n", + " 'n821',\n", + " 'n822',\n", + " 'n823',\n", + " 'n824',\n", + " 'n825',\n", + " 'n826',\n", + " 'n827',\n", + " 'n828',\n", + " 'n829',\n", + " 'n830',\n", + " 'n831',\n", + " 'n832',\n", + " 'n833',\n", + " 'n834',\n", + " 'n835',\n", + " 'n836',\n", + " 'n837',\n", + " 'n838',\n", + " 'n839',\n", + " 'n840',\n", + " 'n841',\n", + " 'n842',\n", + " 'n843',\n", + " 'n844',\n", + " 'n845',\n", + " 'n846',\n", + " 'n847',\n", + " 'n848',\n", + " 'n849',\n", + " 'n850',\n", + " 'n851',\n", + " 'n852',\n", + " 'n853',\n", + " 'n854',\n", + " 'n855',\n", + " 'n856',\n", + " 'n857',\n", + " 'n858',\n", + " 'n859',\n", + " 'n860',\n", + " 'n861',\n", + " 'n862',\n", + " 'n863',\n", + " 'n864',\n", + " 'n865',\n", + " 'n866',\n", + " 'n867',\n", + " 'n868',\n", + " 'n869',\n", + " 'n870',\n", + " 'n871',\n", + " 'n872',\n", + " 'n873',\n", + " 'n874',\n", + " 'n875',\n", + " 'n876',\n", + " 'n877',\n", + " 'n878',\n", + " 'n879',\n", + " 'n880',\n", + " 'n881',\n", + " 'n882',\n", + " 'n883',\n", + " 'n884',\n", + " 'n885',\n", + " 'n886',\n", + " 'n887',\n", + " 'n888',\n", + " 'n889',\n", + " 'n890',\n", + " 'n891',\n", + " 'n892',\n", + " 'n893',\n", + " 'n894',\n", + " 'n895',\n", + " 'n896',\n", + " 'n897',\n", + " 'n898',\n", + " 'n899',\n", + " 'n900',\n", + " 'n901',\n", + " 'n902',\n", + " 'n903',\n", + " 'n904',\n", + " 'n905',\n", + " 'n906',\n", + " 'n907',\n", + " 'n908',\n", + " 'n909',\n", + " 'n910',\n", + " 'n911',\n", + " 'n912',\n", + " 'n913',\n", + " 'n914',\n", + " 'n915',\n", + " 'n916',\n", + " 'n917',\n", + " 'n918',\n", + " 'n919',\n", + " 'n920',\n", + " 'n921',\n", + " 'n922',\n", + " 'n923',\n", + " 'n924',\n", + " 'n925',\n", + " 'n926',\n", + " 'n927',\n", + " 'n928',\n", + " 'n929',\n", + " 'n930',\n", + " 'n931',\n", + " 'n932',\n", + " 'n933',\n", + " 'n934',\n", + " 'n935',\n", + " 'n936',\n", + " 'n937',\n", + " 'n938',\n", + " 'n939',\n", + " 'n940',\n", + " 'n941',\n", + " 'n942',\n", + " 'n943',\n", + " 'n944',\n", + " 'n945',\n", + " 'n946',\n", + " 'n947',\n", + " 'n948',\n", + " 'n949',\n", + " 'n950',\n", + " 'n951',\n", + " 'n952',\n", + " 'n953',\n", + " 'n954',\n", + " 'n955',\n", + " 'n956',\n", + " 'n957',\n", + " 'n958',\n", + " 'n959',\n", + " 'n960',\n", + " 'n961',\n", + " 'n962',\n", + " 'n963',\n", + " 'n964',\n", + " 'n965',\n", + " 'n966',\n", + " 'n967',\n", + " 'n968',\n", + " 'n969',\n", + " 'n970',\n", + " 'n971',\n", + " 'n972',\n", + " 'n973',\n", + " 'n974',\n", + " 'n975',\n", + " 'n976',\n", + " 'n977',\n", + " 'n978',\n", + " 'n979',\n", + " 'n980',\n", + " 'n981',\n", + " 'n982',\n", + " 'n983',\n", + " 'n984',\n", + " 'n985',\n", + " 'n986',\n", + " 'n987',\n", + " 'n988',\n", + " 'n989',\n", + " 'n990',\n", + " 'n991',\n", + " 'n992',\n", + " 'n993',\n", + " 'n994',\n", + " 'n995',\n", + " 'n996',\n", + " 'n997',\n", + " 'n998',\n", + " 'n999',\n", + " ...]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a2_names" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -236,9 +1343,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Feature columns do not sum to 1.0 for all samples - so they are being transformed.\n", + "Train: (3170, 5654), Test: (779, 5654)\n" + ] + } + ], "source": [ "# load metadata\n", "target = \"age_months\"\n", @@ -254,7 +1370,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -271,7 +1387,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -287,9 +1403,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array(['g__Fusobacterium', 'g__Rheinheimera', 's__uncultured_bacterium',\n", + " ..., 'n5575', 'n5576', 'n5577'], dtype='" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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KT799FNERkbi+vXraNeuXYXGR0RERET0PDO46ZXS0tLQt29fFBQUQETQpEkTbNiwAQAwd+5chIWFoWnTpjA1NcXGjRs5gxQRERERUQUyuHffTZo0QUxMTKll9evXx+7duys4IiIiIiIiKqLRIG8iIiIiIiJNlPsOxu3bt3H58uUSi90REREREVV11XkQdmXT6g5Gly5dcOvWLWRnZ8PHxwc9evTAJ598oq/YiIiIiIjIwGiVYKSlpcHa2ho7d+7E66+/joSEBPznP//RU2hERERERGRotEow8vLyADxa6K5z584wMTEpsbgdERERERE9v7RKMDw9PdGlSxf8/PPP6NSpE3JycphgEBERERGRilaDvOfNm4fo6Gj4+PjAzMwMV69excSJE/UVGxERERERGRit7mD06tULvXr1grOzMwCgUaNGWLhwoV4CIyIiIiIiw6PRHYz8/Hw8fPgQhYWFuH//PkQEwKOpanNycvQaIBERERERGQ6N7mDMnj0bFhYWOH36NMzNzWFhYQELCwt4eHhgyJAh+o6RiIiIiIgMhEYJxowZM1BYWIgRI0agsLBQ9bh16xamT5+u7xiJiIiIiMhAaDXIe/ny5SgsLMT169eRn5+v2u7g4KDzwIiIiIiIyPBolWCsX78eY8eOhbGxMYyMjAAACoUCN27c0EtwRES65DTlF43qJc/prudIiIiIqi+tEoxZs2bh2LFjcHd311c8RERERERkwLSaptbOzo7JBRERERERlUmrBKNPnz5YsmQJMjMzkZOTo3oQEREREREBWn5FasqUKQCA9957DwqFAiIChUKBgoICvQRHRERERESGRasEo7CwUF9xEBERERFRNaBVggEAN27cQHx8PNq3b4/8/HwUFhbC1NRUH7ERERERUTlw1jyqTFqNwfj3v/+NVq1aISwsDABw9uxZ9OrVSx9xERERERGRAdLqDsYXX3yBqKgovPLKKwAAHx8fXLp0SS+BlVdCQgLCw8ORkZEBa2trrFu3Ds2bN6/ssIiIiIhIj3jXpurQ6g5GjRo1YGtrq7atqn096h//+AdGjBiB8+fPY/LkyRg+fHhlh0RERERE9NzQ6g6GpaUl0tLSoFAoAAD79u2DjY2NXgIrjxs3biA6Ohq7d+8GAPTt2xdjxoxBcnIynJycKjc4Ii3xkxgiIqoo/JtDuqRVgjF37lx069YN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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " CROSS VALIDATION : \n", + " Intercept : 8.163837992584526\n", + " Selected variables : s__uncultured_Dorea s__Lactobacillus_mucosae s__Lactobacillus_ruminis g__Blautia g__Dialister g__Blautia f__Enterobacteriaceae g__Romboutsia n6 n7 n89 n119 n157 n158 n163 n213 n635 n656 n658 n727 n805 n952 n1030 n1166 n1203 n1204 n1208 n1218 n1328 n1351 n1435 n1482 n1489 n1511 n1553 n1559 n1571 n1585 n1622 n1644 n1687 n1717 n1718 n1719 n1834 n2101 n2946 n2947 n3314 n4126 n4215 n4901 n5142 n5567 \n", + " Running time : 477.723s\n", + "\n" + ] + } + ], "source": [ "problem.solve()\n", "# todo: find out how to extract the insights from the model to disk without changing classo\n", @@ -358,7 +1541,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -373,9 +1556,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['d__Bacteria; p__Firmicutes; c__Clostridia; o__Lachnospirales; f__Lachnospiraceae; g__Dorea; s__uncultured_Dorea'\n", + " 'd__Bacteria; p__Firmicutes; c__Bacilli; o__Lactobacillales; f__Lactobacillaceae; g__Lactobacillus; s__Lactobacillus_mucosae'\n", + " 'd__Bacteria; p__Firmicutes; c__Bacilli; o__Lactobacillales; f__Lactobacillaceae; g__Lactobacillus; s__Lactobacillus_ruminis'\n", + " 'd__Bacteria; p__Firmicutes; c__Clostridia; o__Lachnospirales; f__Lachnospiraceae; g__Blautia'\n", + " 'd__Bacteria; p__Firmicutes; c__Negativicutes; o__Veillonellales-Selenomonadales; f__Veillonellaceae; g__Dialister'\n", + " 'd__Bacteria; p__Firmicutes; c__Clostridia; o__Lachnospirales; f__Lachnospiraceae; g__Blautia'\n", + " 'd__Bacteria; p__Proteobacteria; c__Gammaproteobacteria; o__Enterobacterales; f__Enterobacteriaceae'\n", + " 'd__Bacteria; p__Firmicutes; c__Clostridia; o__Peptostreptococcales-Tissierellales; f__Peptostreptococcaceae; g__Romboutsia'\n", + " 'n6' 'n7' 'n89' 'n119' 'n157' 'n158' 'n163' 'n213' 'n635' 'n656' 'n658'\n", + " 'n727' 'n805' 'n952' 'n1030' 'n1166' 'n1203' 'n1204' 'n1208' 'n1218'\n", + " 'n1328' 'n1351' 'n1435' 'n1482' 'n1489' 'n1511' 'n1553' 'n1559' 'n1571'\n", + " 'n1585' 'n1622' 'n1644' 'n1687' 'n1717' 'n1718' 'n1719' 'n1834' 'n2101'\n", + " 'n2946' 'n2947' 'n3314' 'n4126' 'n4215' 'n4901' 'n5142' 'n5567']\n" + ] + } + ], "source": [ "# ! class solution_CV: defined in @solver.py L930\n", "selection = problem.solution.CV.selected_param[1:] # exclude the intercept\n", @@ -385,7 +1588,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -398,11 +1601,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# save model: A, label, alpha (includes selected_ft)\n", + "# todo: adjust path\n", "path2out = \"test_model\"\n", "if not os.path.exists(path2out):\n", " os.makedirs(path2out)\n", @@ -414,7 +1618,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -441,7 +1645,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ diff --git a/q2_ritme/model_space/_static_trainables.py b/q2_ritme/model_space/_static_trainables.py index 438d07d..2e3f01f 100644 --- a/q2_ritme/model_space/_static_trainables.py +++ b/q2_ritme/model_space/_static_trainables.py @@ -8,6 +8,7 @@ import numpy as np import pandas as pd import ray +import skbio import torch import xgboost as xgb from coral_pytorch.dataset import corn_label_from_logits @@ -103,6 +104,8 @@ def train_linreg( host_id: str, seed_data: int, seed_model: int, + tax: pd.DataFrame, + tree_phylo: skbio.TreeNode, ) -> None: """ Train a linear regression model and report the results to Ray Tune. @@ -142,6 +145,8 @@ def train_rf( host_id: str, seed_data: int, seed_model: int, + tax: pd.DataFrame, + tree_phylo: skbio.TreeNode, ) -> None: """ Train a random forest model and report the results to Ray Tune. @@ -275,7 +280,13 @@ def load_data(X_train, y_train, X_val, y_val, y_type, config): def train_nn( - config, train_val, target, host_id, seed_data, seed_model, nn_type="regression" + config, + train_val, + target, + host_id, + seed_data, + seed_model, + nn_type="regression", ): # Set the seed for reproducibility seed_everything(seed_model, workers=True) @@ -362,13 +373,17 @@ def train_nn( trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) -def train_nn_reg(config, train_val, target, host_id, seed_data, seed_model): +def train_nn_reg( + config, train_val, target, host_id, seed_data, seed_model, tax, tree_phylo +): train_nn( config, train_val, target, host_id, seed_data, seed_model, nn_type="regression" ) -def train_nn_class(config, train_val, target, host_id, seed_data, seed_model): +def train_nn_class( + config, train_val, target, host_id, seed_data, seed_model, tax, tree_phylo +): train_nn( config, train_val, @@ -380,7 +395,9 @@ def train_nn_class(config, train_val, target, host_id, seed_data, seed_model): ) -def train_nn_corn(config, train_val, target, host_id, seed_data, seed_model): +def train_nn_corn( + config, train_val, target, host_id, seed_data, seed_model, tax, tree_phylo +): # corn model from https://github.com/Raschka-research-group/coral-pytorch train_nn( config, @@ -400,6 +417,8 @@ def train_xgb( host_id: str, seed_data: int, seed_model: int, + tax: pd.DataFrame, + tree_phylo: skbio.TreeNode, ) -> None: """ Train an XGBoost model and report the results to Ray Tune. diff --git a/q2_ritme/process_data.py b/q2_ritme/process_data.py index 07ff692..e8395b6 100644 --- a/q2_ritme/process_data.py +++ b/q2_ritme/process_data.py @@ -1,6 +1,8 @@ import biom import pandas as pd import qiime2 as q2 +import skbio +from qiime2.plugins import phylogeny from sklearn.model_selection import GroupShuffleSplit # todo: adjust to json file to be read in from user @@ -85,6 +87,51 @@ def load_data( return ft, md +def load_tax_phylo( + path2tax: str, path2phylo: str, ft: pd.DataFrame +) -> (pd.DataFrame, skbio.TreeNode): + """ + Load taxonomy and phylogeny data. + """ + # todo: add option for simulated data + if path2tax and path2phylo: + # taxonomy + art_taxonomy = q2.Artifact.load(path2tax) + df_tax = art_taxonomy.view(pd.DataFrame) + # rename taxonomy to match new "F" feature names + df_tax.index = df_tax.index.map(lambda x: "F" + str(x)) + + # Filter the taxonomy based on the feature table + df_tax_f = df_tax[df_tax.index.isin(ft.columns.tolist())] + + if df_tax_f.shape[0] == 0: + raise ValueError("Taxonomy data does not match with feature table.") + + # phylogeny + art_phylo = q2.Artifact.load(path2phylo) + # filter tree by feature table: this prunes a phylogenetic tree to match + # the input ids + # Remove the first letter of each column name: "F" to match phylotree + ft.columns = [col[1:] for col in ft.columns] + art_ft = q2.Artifact.import_data("FeatureTable[RelativeFrequency]", ft) + + (art_phylo_f,) = phylogeny.actions.filter_tree(tree=art_phylo, table=art_ft) + tree_phylo_f = art_phylo_f.view(skbio.TreeNode) + + # add prefix "F" to leaf names in tree to remain consistent with ft + for node in tree_phylo_f.tips(): + node.name = "F" + node.name + + # ensure that # leaves in tree == feature table dimension + num_leaves = tree_phylo_f.count(tips=True) + assert num_leaves == ft.shape[1] + else: + raise ValueError( + "Simulation of taxonomy and phylogeny data not implemented yet." + ) + return df_tax_f, tree_phylo_f + + def filter_merge_n_sort( md: pd.DataFrame, ft: pd.DataFrame, @@ -151,12 +198,14 @@ def split_data_by_host( def load_n_split_data( path2md: str, path2ft: str, + path2tax: str, + path2phylo: str, host_id: str, target: str, train_size: float, seed: int, filter_md_cols: list = None, -) -> (pd.DataFrame, pd.DataFrame): +) -> (pd.DataFrame, pd.DataFrame, pd.DataFrame, skbio.TreeNode): """ Load, merge and sort data, then split into train-test sets by host_id. @@ -165,6 +214,10 @@ def load_n_split_data( is used. path2ft (str, optional): Path to features file. If None, simulated data is used. + path2tax (str, optional): Path to taxonomy file. If None, simulated data + is used. + path2phylo (str, optional): Path to phylogeny file. If None, simulated data + is used. host_id (str, optional): ID of the host. Default is HOST_ID from config. target (str, optional): Name of target variable. Default is TARGET from config. @@ -176,13 +229,16 @@ def load_n_split_data( SEED_DATA from config. Returns: - tuple: A tuple containing train and test dataframes. + : Train and test dataframes as well as matching taxonomy and phylogeny. """ ft, md = load_data(path2md, path2ft, target) + # tax: n_features x ("Taxon", "Confidence") + # tree_phylo: n_features leaf nodes + tax, tree_phylo = load_tax_phylo(path2tax, path2phylo, ft) data = filter_merge_n_sort(md, ft, host_id, target, filter_md_cols) # todo: add split also by study_id train_val, test = split_data_by_host(data, host_id, train_size, seed) - return train_val, test + return train_val, test, tax, tree_phylo diff --git a/q2_ritme/run_config.json b/q2_ritme/run_config.json index c20fb1b..0691857 100644 --- a/q2_ritme/run_config.json +++ b/q2_ritme/run_config.json @@ -1,24 +1,16 @@ { - "experiment_tag": "test_synthetic", + "experiment_tag": "test_5c_trac", "host_id": "host_id", "ls_model_types": [ - "linreg", - "xgb", - "nn_reg", - "nn_class", - "nn_corn", - "rf" + "linreg" ], "mlflow_tracking_uri": "mlruns", - "models_to_evaluate_separately": [ - "xgb", - "nn_reg", - "nn_class", - "nn_corn" - ], + "models_to_evaluate_separately": [], "num_trials": 1, - "path_to_ft": null, - "path_to_md": null, + "path_to_ft": "experiments/data/220728_monthly/all_otu_table_filt.qza", + "path_to_md": "experiments/data/220728_monthly/metadata_proc_v20240323_r0_r3_le_2yrs.tsv", + "path_to_phylo": "experiments/data/220728_monthly/silva-138-99-rooted-tree.qza", + "path_to_tax": "experiments/data/220728_monthly/otu_taxonomy_all.qza", "seed_data": 12, "seed_model": 12, "target": "age_months", diff --git a/q2_ritme/run_n_eval_tune.py b/q2_ritme/run_n_eval_tune.py index e12ec63..a7aeac6 100644 --- a/q2_ritme/run_n_eval_tune.py +++ b/q2_ritme/run_n_eval_tune.py @@ -43,14 +43,15 @@ def run_n_eval_tune(config_path): "Please use another one." ) - # todo: flag mlflow runs also with experiment tag somehow path_mlflow = os.path.join("experiments", config["mlflow_tracking_uri"]) path_exp = os.path.join(base_path, config["experiment_tag"]) # ! Load and split data - train_val, test = load_n_split_data( + train_val, test, tax, tree_phylo = load_n_split_data( config["path_to_md"], config["path_to_ft"], + config["path_to_tax"], + config["path_to_phylo"], config["host_id"], config["target"], config["train_size"], @@ -64,6 +65,8 @@ def run_n_eval_tune(config_path): config["host_id"], config["seed_data"], config["seed_model"], + tax, + tree_phylo, path_mlflow, path_exp, # number of trials to run per model type * grid_search parameters in diff --git a/q2_ritme/tune_models.py b/q2_ritme/tune_models.py index 3c1221f..748ca0b 100644 --- a/q2_ritme/tune_models.py +++ b/q2_ritme/tune_models.py @@ -3,6 +3,7 @@ import numpy as np import pandas as pd +import skbio import torch from ray import air, init, shutdown, tune from ray.air.integrations.mlflow import MLflowLoggerCallback @@ -39,6 +40,8 @@ def run_trials( host_id, seed_data, seed_model, + tax, + tree_phylo, path2exp, num_trials, fully_reproducible=False, # if True hyperband instead of ASHA scheduler is used @@ -97,6 +100,8 @@ def run_trials( host_id=host_id, seed_data=seed_data, seed_model=seed_model, + tax=tax, + phylo=tree_phylo, ), resources, ), @@ -147,6 +152,8 @@ def run_all_trials( host_id: str, seed_data: int, seed_model: int, + tax: pd.DataFrame, + tree_phylo: skbio.TreeNode, mlflow_uri: str, path_exp: str, num_trials: int, @@ -170,6 +177,8 @@ def run_all_trials( host_id, seed_data, seed_model, + tax, + tree_phylo, path_exp, num_trials, fully_reproducible=fully_reproducible, From 6e0398cd031334bdad27995e8fab425e517c09f0 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Fri, 3 May 2024 21:26:14 +0200 Subject: [PATCH 06/28] newest conda env setup --- ci/recipe/meta.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/recipe/meta.yaml b/ci/recipe/meta.yaml index 6ae7e65..c4d364e 100644 --- a/ci/recipe/meta.yaml +++ b/ci/recipe/meta.yaml @@ -22,7 +22,6 @@ requirements: - importlib-metadata - qiime2 {{ qiime2_epoch }}.* - q2-feature-table {{ qiime2_epoch }}.* - - q2-feature-classifier {{ qiime2_epoch }}.* - q2-phylogeny {{ qiime2_epoch }}.* # todo: check if q2-types is really needed - if not remove - q2-types {{ qiime2_epoch }}.* @@ -48,6 +47,8 @@ requirements: - pip: - coral_pytorch - c-lasso + # grpcio pinned due to incompatibility with ray caused by c-lasso + - grpcio==1.51.1 test: From dc40d3cef140b4d97e2f55f2f4d7c4b88613e295 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Fri, 3 May 2024 21:40:37 +0200 Subject: [PATCH 07/28] add trac model --- experiments/implement_matrixA.ipynb | 1422 ++----------------- q2_ritme/evaluate_models.py | 28 + q2_ritme/feature_space/_process_train.py | 109 +- q2_ritme/model_space/_static_searchspace.py | 24 + q2_ritme/model_space/_static_trainables.py | 106 +- q2_ritme/process_data.py | 7 +- q2_ritme/run_config.json | 4 +- q2_ritme/tune_models.py | 13 +- 8 files changed, 424 insertions(+), 1289 deletions(-) diff --git a/experiments/implement_matrixA.ipynb b/experiments/implement_matrixA.ipynb index 99f05ba..58ad790 100644 --- a/experiments/implement_matrixA.ipynb +++ b/experiments/implement_matrixA.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -11,6 +11,8 @@ "import pandas as pd\n", "import qiime2 as q2\n", "import skbio\n", + "import pickle\n", + "\n", "from classo import classo_problem\n", "from qiime2.plugins import phylogeny\n", "from skbio import TreeNode\n", @@ -23,7 +25,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -75,21 +77,11 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " /-f1\n", - " /n1------|\n", - "-n2------| \\-f2\n", - " |\n", - " \\-f3\n" - ] - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "# Create the tree nodes with lengths\n", "n1 = TreeNode(name=\"n1\")\n", @@ -111,22 +103,11 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1., 0., 0., 1.],\n", - " [0., 1., 0., 1.],\n", - " [0., 0., 1., 0.]])" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "A_example, a2_names_ex = create_matrix_from_tree(tree)\n", "A_example" @@ -134,20 +115,11 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['n0']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "a2_names_ex" ] @@ -161,20 +133,11 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(9478, 5580)" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "# read feature table\n", "art_feature_table = q2.Artifact.load(\"data/220728_monthly/all_otu_table_filt.qza\")\n", @@ -184,18 +147,11 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(5608, 2)\n", - "(5580, 2)\n" - ] - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "# read taxonomy\n", "path_to_taxonomy = \"data/220728_monthly/otu_taxonomy_all.qza\"\n", @@ -210,20 +166,22 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "870198" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "df_taxonomy_f" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "# read silva phylo tree\n", "path_to_phylo = \"data/220728_monthly/silva-138-99-rooted-tree.qza\"\n", @@ -235,20 +193,11 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "11159" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "# filter tree by feature table: this prunes a phylogenetic tree to match the\n", "# input ids\n", @@ -261,7 +210,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -272,1055 +221,21 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1., 0., 0., ..., 0., 0., 0.],\n", - " [0., 1., 0., ..., 0., 0., 0.],\n", - " [0., 0., 1., ..., 0., 0., 0.],\n", - " ...,\n", - " [0., 0., 0., ..., 0., 1., 1.],\n", - " [0., 0., 0., ..., 1., 1., 1.],\n", - " [0., 0., 0., ..., 1., 1., 1.]])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A, a2_names = create_matrix_from_tree(tree_phylo_f)\n", - "A" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['n0',\n", - " 'n1',\n", - " 'n2',\n", - " 'n3',\n", - " 'n4',\n", - " 'n5',\n", - " 'n6',\n", - " 'n7',\n", - " 'n8',\n", - " 'n9',\n", - " 'n10',\n", - " 'n11',\n", - " 'n12',\n", - " 'n13',\n", - " 'n14',\n", - " 'n15',\n", - " 'n16',\n", - " 'n17',\n", - " 'n18',\n", - " 'n19',\n", - " 'n20',\n", - " 'n21',\n", - " 'n22',\n", - " 'n23',\n", - " 'n24',\n", - " 'n25',\n", - " 'n26',\n", - " 'n27',\n", - " 'n28',\n", - " 'n29',\n", - " 'n30',\n", - " 'n31',\n", - " 'n32',\n", - " 'n33',\n", - " 'n34',\n", - " 'n35',\n", - " 'n36',\n", - " 'n37',\n", - " 'n38',\n", - " 'n39',\n", - " 'n40',\n", - " 'n41',\n", - " 'n42',\n", - " 'n43',\n", - " 'n44',\n", - " 'n45',\n", - " 'n46',\n", - " 'n47',\n", - " 'n48',\n", - " 'n49',\n", - " 'n50',\n", - " 'n51',\n", - " 'n52',\n", - " 'n53',\n", - " 'n54',\n", - " 'n55',\n", - " 'n56',\n", - " 'n57',\n", - " 'n58',\n", - " 'n59',\n", - " 'n60',\n", - " 'n61',\n", - " 'n62',\n", - " 'n63',\n", - " 'n64',\n", - " 'n65',\n", - " 'n66',\n", - " 'n67',\n", - " 'n68',\n", - " 'n69',\n", - " 'n70',\n", - " 'n71',\n", - " 'n72',\n", - " 'n73',\n", - " 'n74',\n", - " 'n75',\n", - " 'n76',\n", - " 'n77',\n", - " 'n78',\n", - " 'n79',\n", - " 'n80',\n", - " 'n81',\n", - " 'n82',\n", - " 'n83',\n", - " 'n84',\n", - " 'n85',\n", - " 'n86',\n", - " 'n87',\n", - " 'n88',\n", - " 'n89',\n", - " 'n90',\n", - " 'n91',\n", - " 'n92',\n", - " 'n93',\n", - " 'n94',\n", - " 'n95',\n", - " 'n96',\n", - " 'n97',\n", - " 'n98',\n", - " 'n99',\n", - " 'n100',\n", - " 'n101',\n", - " 'n102',\n", - " 'n103',\n", - " 'n104',\n", - " 'n105',\n", - " 'n106',\n", - " 'n107',\n", - " 'n108',\n", - " 'n109',\n", - " 'n110',\n", - " 'n111',\n", - " 'n112',\n", - " 'n113',\n", - " 'n114',\n", - " 'n115',\n", - " 'n116',\n", - " 'n117',\n", - " 'n118',\n", - " 'n119',\n", - " 'n120',\n", - " 'n121',\n", - " 'n122',\n", - " 'n123',\n", - " 'n124',\n", - " 'n125',\n", - " 'n126',\n", - " 'n127',\n", - " 'n128',\n", - " 'n129',\n", - " 'n130',\n", - " 'n131',\n", - " 'n132',\n", - " 'n133',\n", - " 'n134',\n", - " 'n135',\n", - " 'n136',\n", - " 'n137',\n", - " 'n138',\n", - " 'n139',\n", - " 'n140',\n", - " 'n141',\n", - " 'n142',\n", - " 'n143',\n", - " 'n144',\n", - " 'n145',\n", - " 'n146',\n", - " 'n147',\n", - " 'n148',\n", - " 'n149',\n", - " 'n150',\n", - " 'n151',\n", - " 'n152',\n", - " 'n153',\n", - " 'n154',\n", - " 'n155',\n", - " 'n156',\n", - " 'n157',\n", - " 'n158',\n", - " 'n159',\n", - " 'n160',\n", - " 'n161',\n", - " 'n162',\n", - " 'n163',\n", - " 'n164',\n", - " 'n165',\n", - " 'n166',\n", - " 'n167',\n", - " 'n168',\n", - " 'n169',\n", - " 'n170',\n", - " 'n171',\n", - " 'n172',\n", - " 'n173',\n", - " 'n174',\n", - " 'n175',\n", - " 'n176',\n", - " 'n177',\n", - " 'n178',\n", - " 'n179',\n", - " 'n180',\n", - " 'n181',\n", - " 'n182',\n", - " 'n183',\n", - " 'n184',\n", - " 'n185',\n", - " 'n186',\n", - " 'n187',\n", - " 'n188',\n", - " 'n189',\n", - " 'n190',\n", - " 'n191',\n", - " 'n192',\n", - " 'n193',\n", - " 'n194',\n", - " 'n195',\n", - " 'n196',\n", - " 'n197',\n", - " 'n198',\n", - " 'n199',\n", - " 'n200',\n", - " 'n201',\n", - " 'n202',\n", - " 'n203',\n", - " 'n204',\n", - " 'n205',\n", - " 'n206',\n", - " 'n207',\n", - " 'n208',\n", - " 'n209',\n", - " 'n210',\n", - " 'n211',\n", - " 'n212',\n", - " 'n213',\n", - " 'n214',\n", - " 'n215',\n", - " 'n216',\n", - " 'n217',\n", - " 'n218',\n", - " 'n219',\n", - " 'n220',\n", - " 'n221',\n", - " 'n222',\n", - " 'n223',\n", - " 'n224',\n", - " 'n225',\n", - " 'n226',\n", - " 'n227',\n", - " 'n228',\n", - " 'n229',\n", - " 'n230',\n", - " 'n231',\n", - " 'n232',\n", - " 'n233',\n", - " 'n234',\n", - " 'n235',\n", - " 'n236',\n", - " 'n237',\n", - " 'n238',\n", - " 'n239',\n", - " 'n240',\n", - " 'n241',\n", - " 'n242',\n", - " 'n243',\n", - " 'n244',\n", - " 'n245',\n", - " 'n246',\n", - " 'n247',\n", - " 'n248',\n", - " 'n249',\n", - " 'n250',\n", - " 'n251',\n", - " 'n252',\n", - " 'n253',\n", - " 'n254',\n", - " 'n255',\n", - " 'n256',\n", - " 'n257',\n", - " 'n258',\n", - " 'n259',\n", - " 'n260',\n", - " 'n261',\n", - " 'n262',\n", - " 'n263',\n", - " 'n264',\n", - " 'n265',\n", - " 'n266',\n", - " 'n267',\n", - " 'n268',\n", - " 'n269',\n", - " 'n270',\n", - " 'n271',\n", - " 'n272',\n", - " 'n273',\n", - " 'n274',\n", - " 'n275',\n", - " 'n276',\n", - " 'n277',\n", - " 'n278',\n", - " 'n279',\n", - " 'n280',\n", - " 'n281',\n", - " 'n282',\n", - " 'n283',\n", - " 'n284',\n", - " 'n285',\n", - " 'n286',\n", - " 'n287',\n", - " 'n288',\n", - " 'n289',\n", - " 'n290',\n", - " 'n291',\n", - " 'n292',\n", - " 'n293',\n", - " 'n294',\n", - " 'n295',\n", - " 'n296',\n", - " 'n297',\n", - " 'n298',\n", - " 'n299',\n", - " 'n300',\n", - " 'n301',\n", - " 'n302',\n", - " 'n303',\n", - " 'n304',\n", - " 'n305',\n", - " 'n306',\n", - " 'n307',\n", - " 'n308',\n", - " 'n309',\n", - " 'n310',\n", - " 'n311',\n", - " 'n312',\n", - " 'n313',\n", - " 'n314',\n", - " 'n315',\n", - " 'n316',\n", - " 'n317',\n", - " 'n318',\n", - " 'n319',\n", - " 'n320',\n", - " 'n321',\n", - " 'n322',\n", - " 'n323',\n", - " 'n324',\n", - " 'n325',\n", - " 'n326',\n", - " 'n327',\n", - " 'n328',\n", - " 'n329',\n", - " 'n330',\n", - " 'n331',\n", - " 'n332',\n", - " 'n333',\n", - " 'n334',\n", - " 'n335',\n", - " 'n336',\n", - " 'n337',\n", - " 'n338',\n", - " 'n339',\n", - " 'n340',\n", - " 'n341',\n", - " 'n342',\n", - " 'n343',\n", - " 'n344',\n", - " 'n345',\n", - " 'n346',\n", - " 'n347',\n", - " 'n348',\n", - " 'n349',\n", - " 'n350',\n", - " 'n351',\n", - " 'n352',\n", - " 'n353',\n", - " 'n354',\n", - " 'n355',\n", - " 'n356',\n", - " 'n357',\n", - " 'n358',\n", - " 'n359',\n", - " 'n360',\n", - " 'n361',\n", - " 'n362',\n", - " 'n363',\n", - " 'n364',\n", - " 'n365',\n", - " 'n366',\n", - " 'n367',\n", - " 'n368',\n", - " 'n369',\n", - " 'n370',\n", - " 'n371',\n", - " 'n372',\n", - " 'n373',\n", - " 'n374',\n", - " 'n375',\n", - " 'n376',\n", - " 'n377',\n", - " 'n378',\n", - " 'n379',\n", - " 'n380',\n", - " 'n381',\n", - " 'n382',\n", - " 'n383',\n", - " 'n384',\n", - " 'n385',\n", - " 'n386',\n", - " 'n387',\n", - " 'n388',\n", - " 'n389',\n", - " 'n390',\n", - " 'n391',\n", - " 'n392',\n", - " 'n393',\n", - " 'n394',\n", - " 'n395',\n", - " 'n396',\n", - " 'n397',\n", - " 'n398',\n", - " 'n399',\n", - " 'n400',\n", - " 'n401',\n", - " 'n402',\n", - " 'n403',\n", - " 'n404',\n", - " 'n405',\n", - " 'n406',\n", - " 'n407',\n", - " 'n408',\n", - " 'n409',\n", - " 'n410',\n", - " 'n411',\n", - " 'n412',\n", - " 'n413',\n", - " 'n414',\n", - " 'n415',\n", - " 'n416',\n", - " 'n417',\n", - " 'n418',\n", - " 'n419',\n", - " 'n420',\n", - " 'n421',\n", - " 'n422',\n", - " 'n423',\n", - " 'n424',\n", - " 'n425',\n", - " 'n426',\n", - " 'n427',\n", - " 'n428',\n", - " 'n429',\n", - " 'n430',\n", - " 'n431',\n", - " 'n432',\n", - " 'n433',\n", - " 'n434',\n", - " 'n435',\n", - " 'n436',\n", - " 'n437',\n", - " 'n438',\n", - " 'n439',\n", - " 'n440',\n", - " 'n441',\n", - " 'n442',\n", - " 'n443',\n", - " 'n444',\n", - " 'n445',\n", - " 'n446',\n", - " 'n447',\n", - " 'n448',\n", - " 'n449',\n", - " 'n450',\n", - " 'n451',\n", - " 'n452',\n", - " 'n453',\n", - " 'n454',\n", - " 'n455',\n", - " 'n456',\n", - " 'n457',\n", - " 'n458',\n", - " 'n459',\n", - " 'n460',\n", - " 'n461',\n", - " 'n462',\n", - " 'n463',\n", - " 'n464',\n", - " 'n465',\n", - " 'n466',\n", - " 'n467',\n", - " 'n468',\n", - " 'n469',\n", - " 'n470',\n", - " 'n471',\n", - " 'n472',\n", - " 'n473',\n", - " 'n474',\n", - " 'n475',\n", - " 'n476',\n", - " 'n477',\n", - " 'n478',\n", - " 'n479',\n", - " 'n480',\n", - " 'n481',\n", - " 'n482',\n", - " 'n483',\n", - " 'n484',\n", - " 'n485',\n", - " 'n486',\n", - " 'n487',\n", - " 'n488',\n", - " 'n489',\n", - " 'n490',\n", - " 'n491',\n", - " 'n492',\n", - " 'n493',\n", - " 'n494',\n", - " 'n495',\n", - " 'n496',\n", - " 'n497',\n", - " 'n498',\n", - " 'n499',\n", - " 'n500',\n", - " 'n501',\n", - " 'n502',\n", - " 'n503',\n", - " 'n504',\n", - " 'n505',\n", - " 'n506',\n", - " 'n507',\n", - " 'n508',\n", - " 'n509',\n", - " 'n510',\n", - " 'n511',\n", - " 'n512',\n", - " 'n513',\n", - " 'n514',\n", - " 'n515',\n", - " 'n516',\n", - " 'n517',\n", - " 'n518',\n", - " 'n519',\n", - " 'n520',\n", - " 'n521',\n", - " 'n522',\n", - " 'n523',\n", - " 'n524',\n", - " 'n525',\n", - " 'n526',\n", - " 'n527',\n", - " 'n528',\n", - " 'n529',\n", - " 'n530',\n", - " 'n531',\n", - " 'n532',\n", - " 'n533',\n", - " 'n534',\n", - " 'n535',\n", - " 'n536',\n", - " 'n537',\n", - " 'n538',\n", - " 'n539',\n", - " 'n540',\n", - " 'n541',\n", - " 'n542',\n", - " 'n543',\n", - " 'n544',\n", - " 'n545',\n", - " 'n546',\n", - " 'n547',\n", - " 'n548',\n", - " 'n549',\n", - " 'n550',\n", - " 'n551',\n", - " 'n552',\n", - " 'n553',\n", - " 'n554',\n", - " 'n555',\n", - " 'n556',\n", - " 'n557',\n", - " 'n558',\n", - " 'n559',\n", - " 'n560',\n", - " 'n561',\n", - " 'n562',\n", - " 'n563',\n", - " 'n564',\n", - " 'n565',\n", - " 'n566',\n", - " 'n567',\n", - " 'n568',\n", - " 'n569',\n", - " 'n570',\n", - " 'n571',\n", - " 'n572',\n", - " 'n573',\n", - " 'n574',\n", - " 'n575',\n", - " 'n576',\n", - " 'n577',\n", - " 'n578',\n", - " 'n579',\n", - " 'n580',\n", - " 'n581',\n", - " 'n582',\n", - " 'n583',\n", - " 'n584',\n", - " 'n585',\n", - " 'n586',\n", - " 'n587',\n", - " 'n588',\n", - " 'n589',\n", - " 'n590',\n", - " 'n591',\n", - " 'n592',\n", - " 'n593',\n", - " 'n594',\n", - " 'n595',\n", - " 'n596',\n", - " 'n597',\n", - " 'n598',\n", - " 'n599',\n", - " 'n600',\n", - " 'n601',\n", - " 'n602',\n", - " 'n603',\n", - " 'n604',\n", - " 'n605',\n", - " 'n606',\n", - " 'n607',\n", - " 'n608',\n", - " 'n609',\n", - " 'n610',\n", - " 'n611',\n", - " 'n612',\n", - " 'n613',\n", - " 'n614',\n", - " 'n615',\n", - " 'n616',\n", - " 'n617',\n", - " 'n618',\n", - " 'n619',\n", - " 'n620',\n", - " 'n621',\n", - " 'n622',\n", - " 'n623',\n", - " 'n624',\n", - " 'n625',\n", - " 'n626',\n", - " 'n627',\n", - " 'n628',\n", - " 'n629',\n", - " 'n630',\n", - " 'n631',\n", - " 'n632',\n", - " 'n633',\n", - " 'n634',\n", - " 'n635',\n", - " 'n636',\n", - " 'n637',\n", - " 'n638',\n", - " 'n639',\n", - " 'n640',\n", - " 'n641',\n", - " 'n642',\n", - " 'n643',\n", - " 'n644',\n", - " 'n645',\n", - " 'n646',\n", - " 'n647',\n", - " 'n648',\n", - " 'n649',\n", - " 'n650',\n", - " 'n651',\n", - " 'n652',\n", - " 'n653',\n", - " 'n654',\n", - " 'n655',\n", - " 'n656',\n", - " 'n657',\n", - " 'n658',\n", - " 'n659',\n", - " 'n660',\n", - " 'n661',\n", - " 'n662',\n", - " 'n663',\n", - " 'n664',\n", - " 'n665',\n", - " 'n666',\n", - " 'n667',\n", - " 'n668',\n", - " 'n669',\n", - " 'n670',\n", - " 'n671',\n", - " 'n672',\n", - " 'n673',\n", - " 'n674',\n", - " 'n675',\n", - " 'n676',\n", - " 'n677',\n", - " 'n678',\n", - " 'n679',\n", - " 'n680',\n", - " 'n681',\n", - " 'n682',\n", - " 'n683',\n", - " 'n684',\n", - " 'n685',\n", - " 'n686',\n", - " 'n687',\n", - " 'n688',\n", - " 'n689',\n", - " 'n690',\n", - " 'n691',\n", - " 'n692',\n", - " 'n693',\n", - " 'n694',\n", - " 'n695',\n", - " 'n696',\n", - " 'n697',\n", - " 'n698',\n", - " 'n699',\n", - " 'n700',\n", - " 'n701',\n", - " 'n702',\n", - " 'n703',\n", - " 'n704',\n", - " 'n705',\n", - " 'n706',\n", - " 'n707',\n", - " 'n708',\n", - " 'n709',\n", - " 'n710',\n", - " 'n711',\n", - " 'n712',\n", - " 'n713',\n", - " 'n714',\n", - " 'n715',\n", - " 'n716',\n", - " 'n717',\n", - " 'n718',\n", - " 'n719',\n", - " 'n720',\n", - " 'n721',\n", - " 'n722',\n", - " 'n723',\n", - " 'n724',\n", - " 'n725',\n", - " 'n726',\n", - " 'n727',\n", - " 'n728',\n", - " 'n729',\n", - " 'n730',\n", - " 'n731',\n", - " 'n732',\n", - " 'n733',\n", - " 'n734',\n", - " 'n735',\n", - " 'n736',\n", - " 'n737',\n", - " 'n738',\n", - " 'n739',\n", - " 'n740',\n", - " 'n741',\n", - " 'n742',\n", - " 'n743',\n", - " 'n744',\n", - " 'n745',\n", - " 'n746',\n", - " 'n747',\n", - " 'n748',\n", - " 'n749',\n", - " 'n750',\n", - " 'n751',\n", - " 'n752',\n", - " 'n753',\n", - " 'n754',\n", - " 'n755',\n", - " 'n756',\n", - " 'n757',\n", - " 'n758',\n", - " 'n759',\n", - " 'n760',\n", - " 'n761',\n", - " 'n762',\n", - " 'n763',\n", - " 'n764',\n", - " 'n765',\n", - " 'n766',\n", - " 'n767',\n", - " 'n768',\n", - " 'n769',\n", - " 'n770',\n", - " 'n771',\n", - " 'n772',\n", - " 'n773',\n", - " 'n774',\n", - " 'n775',\n", - " 'n776',\n", - " 'n777',\n", - " 'n778',\n", - " 'n779',\n", - " 'n780',\n", - " 'n781',\n", - " 'n782',\n", - " 'n783',\n", - " 'n784',\n", - " 'n785',\n", - " 'n786',\n", - " 'n787',\n", - " 'n788',\n", - " 'n789',\n", - " 'n790',\n", - " 'n791',\n", - " 'n792',\n", - " 'n793',\n", - " 'n794',\n", - " 'n795',\n", - " 'n796',\n", - " 'n797',\n", - " 'n798',\n", - " 'n799',\n", - " 'n800',\n", - " 'n801',\n", - " 'n802',\n", - " 'n803',\n", - " 'n804',\n", - " 'n805',\n", - " 'n806',\n", - " 'n807',\n", - " 'n808',\n", - " 'n809',\n", - " 'n810',\n", - " 'n811',\n", - " 'n812',\n", - " 'n813',\n", - " 'n814',\n", - " 'n815',\n", - " 'n816',\n", - " 'n817',\n", - " 'n818',\n", - " 'n819',\n", - " 'n820',\n", - " 'n821',\n", - " 'n822',\n", - " 'n823',\n", - " 'n824',\n", - " 'n825',\n", - " 'n826',\n", - " 'n827',\n", - " 'n828',\n", - " 'n829',\n", - " 'n830',\n", - " 'n831',\n", - " 'n832',\n", - " 'n833',\n", - " 'n834',\n", - " 'n835',\n", - " 'n836',\n", - " 'n837',\n", - " 'n838',\n", - " 'n839',\n", - " 'n840',\n", - " 'n841',\n", - " 'n842',\n", - " 'n843',\n", - " 'n844',\n", - " 'n845',\n", - " 'n846',\n", - " 'n847',\n", - " 'n848',\n", - " 'n849',\n", - " 'n850',\n", - " 'n851',\n", - " 'n852',\n", - " 'n853',\n", - " 'n854',\n", - " 'n855',\n", - " 'n856',\n", - " 'n857',\n", - " 'n858',\n", - " 'n859',\n", - " 'n860',\n", - " 'n861',\n", - " 'n862',\n", - " 'n863',\n", - " 'n864',\n", - " 'n865',\n", - " 'n866',\n", - " 'n867',\n", - " 'n868',\n", - " 'n869',\n", - " 'n870',\n", - " 'n871',\n", - " 'n872',\n", - " 'n873',\n", - " 'n874',\n", - " 'n875',\n", - " 'n876',\n", - " 'n877',\n", - " 'n878',\n", - " 'n879',\n", - " 'n880',\n", - " 'n881',\n", - " 'n882',\n", - " 'n883',\n", - " 'n884',\n", - " 'n885',\n", - " 'n886',\n", - " 'n887',\n", - " 'n888',\n", - " 'n889',\n", - " 'n890',\n", - " 'n891',\n", - " 'n892',\n", - " 'n893',\n", - " 'n894',\n", - " 'n895',\n", - " 'n896',\n", - " 'n897',\n", - " 'n898',\n", - " 'n899',\n", - " 'n900',\n", - " 'n901',\n", - " 'n902',\n", - " 'n903',\n", - " 'n904',\n", - " 'n905',\n", - " 'n906',\n", - " 'n907',\n", - " 'n908',\n", - " 'n909',\n", - " 'n910',\n", - " 'n911',\n", - " 'n912',\n", - " 'n913',\n", - " 'n914',\n", - " 'n915',\n", - " 'n916',\n", - " 'n917',\n", - " 'n918',\n", - " 'n919',\n", - " 'n920',\n", - " 'n921',\n", - " 'n922',\n", - " 'n923',\n", - " 'n924',\n", - " 'n925',\n", - " 'n926',\n", - " 'n927',\n", - " 'n928',\n", - " 'n929',\n", - " 'n930',\n", - " 'n931',\n", - " 'n932',\n", - " 'n933',\n", - " 'n934',\n", - " 'n935',\n", - " 'n936',\n", - " 'n937',\n", - " 'n938',\n", - " 'n939',\n", - " 'n940',\n", - " 'n941',\n", - " 'n942',\n", - " 'n943',\n", - " 'n944',\n", - " 'n945',\n", - " 'n946',\n", - " 'n947',\n", - " 'n948',\n", - " 'n949',\n", - " 'n950',\n", - " 'n951',\n", - " 'n952',\n", - " 'n953',\n", - " 'n954',\n", - " 'n955',\n", - " 'n956',\n", - " 'n957',\n", - " 'n958',\n", - " 'n959',\n", - " 'n960',\n", - " 'n961',\n", - " 'n962',\n", - " 'n963',\n", - " 'n964',\n", - " 'n965',\n", - " 'n966',\n", - " 'n967',\n", - " 'n968',\n", - " 'n969',\n", - " 'n970',\n", - " 'n971',\n", - " 'n972',\n", - " 'n973',\n", - " 'n974',\n", - " 'n975',\n", - " 'n976',\n", - " 'n977',\n", - " 'n978',\n", - " 'n979',\n", - " 'n980',\n", - " 'n981',\n", - " 'n982',\n", - " 'n983',\n", - " 'n984',\n", - " 'n985',\n", - " 'n986',\n", - " 'n987',\n", - " 'n988',\n", - " 'n989',\n", - " 'n990',\n", - " 'n991',\n", - " 'n992',\n", - " 'n993',\n", - " 'n994',\n", - " 'n995',\n", - " 'n996',\n", - " 'n997',\n", - " 'n998',\n", - " 'n999',\n", - " ...]" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ - "a2_names" + "A, a2_names = create_matrix_from_tree(tree_phylo_f)" ] }, { "cell_type": "code", - "execution_count": 13, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# verification\n", @@ -1343,24 +258,19 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Feature columns do not sum to 1.0 for all samples - so they are being transformed.\n", - "Train: (3170, 5654), Test: (779, 5654)\n" - ] - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "# load metadata\n", "target = \"age_months\"\n", - "train_val, test = load_n_split_data(\n", + "train_val, test, _tx, _phlo = load_n_split_data(\n", " path2md=\"data/220728_monthly/metadata_proc_v20240323_r0_r3_le_2yrs.tsv\",\n", " path2ft=\"data/220728_monthly/all_otu_table_filt.qza\",\n", + " path2tax=\"data/220728_monthly/otu_taxonomy_all.qza\",\n", + " path2phylo=\"data/220728_monthly/silva-138-99-rooted-tree.qza\",\n", " host_id=\"host_id\",\n", " target=target,\n", " train_size=0.8,\n", @@ -1370,8 +280,10 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# preprocess taxonomy aggregation\n", @@ -1387,8 +299,10 @@ }, { "cell_type": "code", - "execution_count": 16, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# perform preprocessing on train\n", @@ -1403,21 +317,11 @@ }, { "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array(['g__Fusobacterium', 'g__Rheinheimera', 's__uncultured_bacterium',\n", - " ..., 'n5575', 'n5576', 'n5577'], dtype='" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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KT799FNERkbi+vXraNeuXYXGR0RERET0PDO46ZXS0tLQt29fFBQUQETQpEkTbNiwAQAwd+5chIWFoWnTpjA1NcXGjRs5gxQRERERUQUyuHffTZo0QUxMTKll9evXx+7duys4IiIiIiIiKqLRIG8iIiIiIiJNlPsOxu3bt3H58uUSi90REREREVV11XkQdmXT6g5Gly5dcOvWLWRnZ8PHxwc9evTAJ598oq/YiIiIiIjIwGiVYKSlpcHa2ho7d+7E66+/joSEBPznP//RU2hERERERGRotEow8vLyADxa6K5z584wMTEpsbgdERERERE9v7RKMDw9PdGlSxf8/PPP6NSpE3JycphgEBERERGRilaDvOfNm4fo6Gj4+PjAzMwMV69excSJE/UVGxERERERGRit7mD06tULvXr1grOzMwCgUaNGWLhwoV4CIyIiIiIiw6PRHYz8/Hw8fPgQhYWFuH//PkQEwKOpanNycvQaIBERERERGQ6N7mDMnj0bFhYWOH36NMzNzWFhYQELCwt4eHhgyJAh+o6RiIiIiIgMhEYJxowZM1BYWIgRI0agsLBQ9bh16xamT5+u7xiJiIiIiMhAaDXIe/ny5SgsLMT169eRn5+v2u7g4KDzwIiIiIiIyPBolWCsX78eY8eOhbGxMYyMjAAACoUCN27c0EtwRES65DTlF43qJc/prudIiIiIqi+tEoxZs2bh2LFjcHd311c8RERERERkwLSaptbOzo7JBRERERERlUmrBKNPnz5YsmQJMjMzkZOTo3oQEREREREBWn5FasqUKQCA9957DwqFAiIChUKBgoICvQRHRERERESGRasEo7CwUF9xEBERERFRNaBVggEAN27cQHx8PNq3b4/8/HwUFhbC1NRUH7ERERERUTlw1jyqTFqNwfj3v/+NVq1aISwsDABw9uxZ9OrVSx9xERERERGRAdLqDsYXX3yBqKgovPLKKwAAHx8fXLp0SS+BlVdCQgLCw8ORkZEBa2trrFu3Ds2bN6/ssIiIiIhIj3jXpurQ6g5GjRo1YGtrq7atqn096h//+AdGjBiB8+fPY/LkyRg+fHhlh0RERERE9NzQ6g6GpaUl0tLSoFAoAAD79u2DjY2NXgIrjxs3biA6Ohq7d+8GAPTt2xdjxoxBcnIynJycKjc4Ii3xkxgiIqoo/JtDuqRVgjF37lx069YN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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " CROSS VALIDATION : \n", - " Intercept : 8.163837992584526\n", - " Selected variables : s__uncultured_Dorea s__Lactobacillus_mucosae s__Lactobacillus_ruminis g__Blautia g__Dialister g__Blautia f__Enterobacteriaceae g__Romboutsia n6 n7 n89 n119 n157 n158 n163 n213 n635 n656 n658 n727 n805 n952 n1030 n1166 n1203 n1204 n1208 n1218 n1328 n1351 n1435 n1482 n1489 n1511 n1553 n1559 n1571 n1585 n1622 n1644 n1687 n1717 n1718 n1719 n1834 n2101 n2946 n2947 n3314 n4126 n4215 n4901 n5142 n5567 \n", - " Running time : 477.723s\n", - "\n" - ] - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "problem.solve()\n", "# todo: find out how to extract the insights from the model to disk without changing classo\n", @@ -1541,8 +394,10 @@ }, { "cell_type": "code", - "execution_count": 20, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# alpha [0] is learned intercept, alpha [1:] are learned coefficients for all features\n", @@ -1556,29 +411,11 @@ }, { "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['d__Bacteria; p__Firmicutes; c__Clostridia; o__Lachnospirales; f__Lachnospiraceae; g__Dorea; s__uncultured_Dorea'\n", - " 'd__Bacteria; p__Firmicutes; c__Bacilli; o__Lactobacillales; f__Lactobacillaceae; g__Lactobacillus; s__Lactobacillus_mucosae'\n", - " 'd__Bacteria; p__Firmicutes; c__Bacilli; o__Lactobacillales; f__Lactobacillaceae; g__Lactobacillus; s__Lactobacillus_ruminis'\n", - " 'd__Bacteria; p__Firmicutes; c__Clostridia; o__Lachnospirales; f__Lachnospiraceae; g__Blautia'\n", - " 'd__Bacteria; p__Firmicutes; c__Negativicutes; o__Veillonellales-Selenomonadales; f__Veillonellaceae; g__Dialister'\n", - " 'd__Bacteria; p__Firmicutes; c__Clostridia; o__Lachnospirales; f__Lachnospiraceae; g__Blautia'\n", - " 'd__Bacteria; p__Proteobacteria; c__Gammaproteobacteria; o__Enterobacterales; f__Enterobacteriaceae'\n", - " 'd__Bacteria; p__Firmicutes; c__Clostridia; o__Peptostreptococcales-Tissierellales; f__Peptostreptococcaceae; g__Romboutsia'\n", - " 'n6' 'n7' 'n89' 'n119' 'n157' 'n158' 'n163' 'n213' 'n635' 'n656' 'n658'\n", - " 'n727' 'n805' 'n952' 'n1030' 'n1166' 'n1203' 'n1204' 'n1208' 'n1218'\n", - " 'n1328' 'n1351' 'n1435' 'n1482' 'n1489' 'n1511' 'n1553' 'n1559' 'n1571'\n", - " 'n1585' 'n1622' 'n1644' 'n1687' 'n1717' 'n1718' 'n1719' 'n1834' 'n2101'\n", - " 'n2946' 'n2947' 'n3314' 'n4126' 'n4215' 'n4901' 'n5142' 'n5567']\n" - ] - } - ], + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], "source": [ "# ! class solution_CV: defined in @solver.py L930\n", "selection = problem.solution.CV.selected_param[1:] # exclude the intercept\n", @@ -1588,8 +425,10 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# # selected lambda with 1-standard-error method\n", @@ -1601,8 +440,10 @@ }, { "cell_type": "code", - "execution_count": 23, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# save model: A, label, alpha (includes selected_ft)\n", @@ -1618,12 +459,14 @@ }, { "cell_type": "code", - "execution_count": 24, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# storing alpha w labels\n", - "idx_alpha = [\"intercept\"] + label.tolist()\n", + "idx_alpha = [\"intercept\"] + df_A_with_labels.columns.tolist()\n", "df_alpha_with_labels = pd.DataFrame(alpha, columns=[\"alpha\"], index=idx_alpha)\n", "df_alpha_with_labels.to_csv(\n", " os.path.join(path2out, \"model_alpha_w_labels.csv\"), index=True\n", @@ -1645,8 +488,10 @@ }, { "cell_type": "code", - "execution_count": 25, - "metadata": {}, + "execution_count": null, + "metadata": { + "metadata": {} + }, "outputs": [], "source": [ "# derive log_geom for test\n", @@ -1661,6 +506,35 @@ "# todo: read alpha\n", "y_test_pred = log_geom_test.dot(alpha[1:]) + alpha[0]" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# test pickle\n", + "# Create a dictionary to store the dataframes\n", + "model = {\"model\": df_alpha_with_labels, \"matrix_a\": df_A_with_labels}\n", + "\n", + "# Serialize the dictionary to a pickle file\n", + "with open(\"data.pkl\", \"wb\") as file:\n", + " pickle.dump(model, file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(\"data.pkl\", \"rb\") as file:\n", + " model = pickle.load(file)" + ] } ], "metadata": { @@ -1679,7 +553,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.19" + "version": "3.8.0" } }, "nbformat": 4, diff --git a/q2_ritme/evaluate_models.py b/q2_ritme/evaluate_models.py index d8886fd..5a04cf2 100644 --- a/q2_ritme/evaluate_models.py +++ b/q2_ritme/evaluate_models.py @@ -1,4 +1,5 @@ import os +import pickle from typing import Any import matplotlib.pyplot as plt @@ -11,6 +12,7 @@ from ray.air.result import Result from sklearn.metrics import mean_squared_error +from q2_ritme.feature_space._process_train import _preprocess_taxonomy_aggregation from q2_ritme.feature_space.transform_features import transform_features from q2_ritme.model_space._static_trainables import NeuralNet @@ -48,6 +50,24 @@ def load_sklearn_model(result: Result) -> Any: return load(result.metrics["model_path"]) +def load_trac_model(result: Result) -> Any: + """ + Load a TRAC model from a given result object. + + :param result: The result object containing the model path. + :return: The loaded TRAC model. + """ + # with pd.HDFStore(result.metrics["model_path"], mode="r") as store: + # alpha_df = store["model"] + # A_df = store["matrix_a"] + # model = {"model": alpha_df, "matrix_a": A_df} + + with open(result.metrics["model_path"], "rb") as file: + model = pickle.load(file) + + return model + + def load_xgb_model(result: Result) -> xgb.Booster: """ Load an XGBoost model from a given result object. @@ -77,6 +97,7 @@ def get_model(model_type: str, result) -> Any: """ model_loaders = { "linreg": load_sklearn_model, + "trac": load_trac_model, "rf": load_sklearn_model, "xgb": load_xgb_model, "nn_reg": load_nn_model, @@ -128,6 +149,13 @@ def predict(self, data): elif self.model.nn_type == "ordinal_regression": logits = self.model(transformed) predicted = corn_label_from_logits(logits).numpy() + elif isinstance(self.model, dict): + # trac model + log_geom, _ = _preprocess_taxonomy_aggregation( + transformed, self.model["matrix_a"].values + ) + alpha = self.model["model"].values + predicted = log_geom.dot(alpha[1:]) + alpha[0] else: predicted = self.model.predict(transformed).flatten() return predicted diff --git a/q2_ritme/feature_space/_process_train.py b/q2_ritme/feature_space/_process_train.py index b7a327a..c697e21 100644 --- a/q2_ritme/feature_space/_process_train.py +++ b/q2_ritme/feature_space/_process_train.py @@ -1,14 +1,74 @@ +import numpy as np +import pandas as pd + from q2_ritme.feature_space.transform_features import transform_features from q2_ritme.process_data import split_data_by_host -def process_train(config, train_val, target, host_id, seed_data): - # todo: adjust feature selection in future to include md - # todo: note -> must also be adjusted in run_n_eval_tune.py +def _create_matrix_from_tree(tree): + # Get all leaves and create a mapping from leaf names to indices + leaves = list(tree.tips()) + leaf_names = [leaf.name for leaf in leaves] + # map each leaf name to unique index + leaf_index_map = {name: idx for idx, name in enumerate(leaf_names)} + + # Get the number of leaves and internal nodes + num_leaves = len(leaf_names) + # root is not included + internal_nodes = list(tree.non_tips()) + + # Create the identity matrix for the leaves: A1 (num_leaves x num_leaves) + A1 = np.eye(num_leaves) + + # Create the matrix for the internal nodes: A2 (num_leaves x + # num_internal_nodes) + # initialise it with zeros + A2 = np.zeros((num_leaves, len(internal_nodes))) + + # Populate A2 with 1s for the leaves linked by each internal node + # iterate over all internal nodes to find descendents of this node and mark + # them accordingly + a2_node_names = [] + for j, node in enumerate(internal_nodes): + # todo: adjust names to consensus taxonomy from descentents + # for now node names are just increasing integers - since node.name is float + a2_node_names.append("n" + str(j)) + descendant_leaves = {leaf.name for leaf in node.tips()} + for leaf_name in leaf_names: + if leaf_name in descendant_leaves: + A2[leaf_index_map[leaf_name], j] = 1 + + # Concatenate A1 and A2 to create the final matrix A + A = np.hstack((A1, A2)) + + return A, a2_node_names + + +def _verify_matrix_a(A, feature_columns, tree_phylo): + # no all 1 in one column + assert not np.any(np.all(A == 1.0, axis=0)) + + # shape should be = feature_count + node_count + nb_features = len(feature_columns) + nb_non_leaf_nodes = len(list(tree_phylo.non_tips())) + + assert nb_features + nb_non_leaf_nodes == A.shape[1] + + +def _preprocess_taxonomy_aggregation(x, A): + pseudo_count = 0.000001 + # ? what happens if x is relative abundances + X = np.log(pseudo_count + x) + nleaves = np.sum(A, axis=0) + log_geom = X.dot(A) / nleaves + + return log_geom, nleaves + + +def _transform_features_in_complete_data(config, train_val, target): features = [x for x in train_val if x.startswith("F")] non_features = [x for x in train_val if x not in features] - # feature engineering method -> config ft_transformed = transform_features( train_val[features], config["data_transform"], @@ -16,8 +76,43 @@ def process_train(config, train_val, target, host_id, seed_data): ) train_val_t = train_val[non_features].join(ft_transformed) - # train & val split - for training purposes + return train_val_t, ft_transformed.columns + + +def process_train(config, train_val, target, host_id, seed_data): + train_val_t, feature_columns = _transform_features_in_complete_data( + config, train_val, target + ) + train, val = split_data_by_host(train_val_t, host_id, 0.8, seed_data) - X_train, y_train = train[ft_transformed.columns], train[target] - X_val, y_val = val[ft_transformed.columns], val[target] + X_train, y_train = train[feature_columns], train[target] + X_val, y_val = val[feature_columns], val[target] return X_train.values, y_train.values, X_val.values, y_val.values + + +def process_train_trac(config, train_val, target, host_id, seed_data, tax, tree_phylo): + train_val_t, feature_columns = _transform_features_in_complete_data( + config, train_val, target + ) + X_train_val, y_train_val = train_val_t[feature_columns], train_val_t[target] + + # no need to split train-val for trac since CV is performed within the model + + # derive matrix A + A, a2_names = _create_matrix_from_tree(tree_phylo) + _verify_matrix_a(A, feature_columns, tree_phylo) + + # get labels for all dimensions of A + label = tax["Taxon"].values + nb_features = len(feature_columns) + assert len(label) == len(feature_columns) + label = np.append(label, a2_names) + assert len(label) == A.shape[1] + A_df = pd.DataFrame(A, columns=label, index=label[:nb_features]) + + # get log_geom + log_geom_trainval, nleaves = _preprocess_taxonomy_aggregation( + X_train_val.values, A_df.values + ) + + return log_geom_trainval, y_train_val, nleaves, A_df diff --git a/q2_ritme/model_space/_static_searchspace.py b/q2_ritme/model_space/_static_searchspace.py index 1d2e045..43ea8f2 100644 --- a/q2_ritme/model_space/_static_searchspace.py +++ b/q2_ritme/model_space/_static_searchspace.py @@ -103,6 +103,29 @@ def get_xgb_space(train_val): ) +def get_trac_space(train_val): + # no feature_transformation to be used for trac + data_eng_space = {"data_transform": None, "data_alr_denom_idx": None} + return dict( + model="trac", + **data_eng_space, + **{ + # 'one-cv_one_stddev-error' = select simplest model (largest lambda + # value) in CV whose CV score is within 1 stddev of best score + # todo: revert back to full choice + "cv_one_stddev": tune.choice([True]), + # "cv_one_stddev": tune.choice([True, False]), + "lambdas_num_searched": tune.choice([10]), + # "lambdas_num_searched": tune.choice([80, 100, 120]), + "lambda_min": tune.choice([0.01]), + # "lambda_min": tune.choice([0.00001, 0.0001, 0.001, 0.01]), + # logscale when going from lambda_min to 1 + "lambda_logscale_search": tune.choice([True]), + # "lambda_logscale_search": tune.choice([True, False]), + }, + ) + + def get_search_space(train_val): return { "xgb": get_xgb_space(train_val), @@ -111,4 +134,5 @@ def get_search_space(train_val): "nn_corn": get_nn_space(train_val, "nn_corn"), "linreg": get_linreg_space(train_val), "rf": get_rf_space(train_val), + "trac": get_trac_space(train_val), } diff --git a/q2_ritme/model_space/_static_trainables.py b/q2_ritme/model_space/_static_trainables.py index 2e3f01f..61db63e 100644 --- a/q2_ritme/model_space/_static_trainables.py +++ b/q2_ritme/model_space/_static_trainables.py @@ -1,6 +1,7 @@ """Module with tune trainables of all static models""" import os +import pickle import random from typing import Any, Dict @@ -11,6 +12,7 @@ import skbio import torch import xgboost as xgb +from classo import classo_problem from coral_pytorch.dataset import corn_label_from_logits from coral_pytorch.losses import corn_loss from pytorch_lightning import LightningModule, Trainer, seed_everything @@ -27,7 +29,7 @@ from torch.optim import Adam from torch.utils.data import DataLoader, TensorDataset -from q2_ritme.feature_space._process_train import process_train +from q2_ritme.feature_space._process_train import process_train, process_train_trac def _predict_rmse(model: BaseEstimator, X: np.ndarray, y: np.ndarray) -> float: @@ -138,6 +140,108 @@ def train_linreg( _report_results_manually(linreg, X_train, y_train, X_val, y_val) +def _report_results_manually_trac(alpha, A_df, log_geom_trainval, y_train_val): + # save coefficients w labels & matrix A with labels -> model_path + idx_alpha = ["intercept"] + A_df.columns.tolist() + df_alpha_with_labels = pd.DataFrame(alpha, columns=["alpha"], index=idx_alpha) + + model = {"model": df_alpha_with_labels, "matrix_a": A_df} + + # save model + path_to_save = ray.train.get_context().get_trial_dir() + model_path = os.path.join(path_to_save, "model.pkl") + with open(model_path, "wb") as file: + pickle.dump(model, file) + # with pd.HDFStore(model_path, mode="w") as store: + # store["model"] = df_alpha_with_labels + # store["matrix_a"] = A_df + + # calculate RMSE + y_pred = log_geom_trainval.dot(alpha[1:]) + alpha[0] + score_train_val = mean_squared_error(y_train_val, y_pred, squared=False) + + session.report( + metrics={ + # todo: check is this a problem that both are given? + "rmse_val": score_train_val, + "rmse_train": score_train_val, + "model_path": model_path, + } + ) + return None + + +def train_trac( + config: Dict[str, Any], + train_val: pd.DataFrame, + target: str, + host_id: str, + seed_data: int, + seed_model: int, + tax: pd.DataFrame, + tree_phylo: skbio.TreeNode, +) -> None: + """ + Train a trac model and report the results to Ray Tune. + + Parameters: + config (Dict[str, Any]): The configuration for the training. + train_val (DataFrame): The training and validation data. + target (str): The target variable. + host_id (str): The host ID. + seed_data (int): The seed for the data. + seed_model (int): The seed for the model. + + Returns: + None + """ + # ! process dataset: X with features & y with host_id + log_geom_trainval, y_train_val, nleaves, A_df = process_train_trac( + config, train_val, target, host_id, seed_data, tax, tree_phylo + ) + + # ! model + np.random.seed(seed_model) + + # perform CV classo: trac + label_short = np.array([la.split(";")[-1].strip() for la in A_df.columns]) + problem = classo_problem(log_geom_trainval, y_train_val.values, label=label_short) + + problem.formulation.w = 1 / nleaves + problem.formulation.intercept = True + problem.formulation.concomitant = False # not relevant for here + + # ! one form of model selection needs to be chosen + # stability selection: for pre-selected range of lambda find beta paths + problem.model_selection.StabSel = False + # calculate coefficients for a grid of lambdas + problem.model_selection.PATH = False + # lambda values checked with CV are `Nlam` points between 1 and `lamin`, with + # logarithm scale or not depending on `logscale`. + problem.model_selection.CV = True + problem.model_selection.CVparameters.seed = ( + seed_model # one could change logscale, Nsubset, oneSE + ) + # 'one-standard-error' = select simplest model (largest lambda value) in CV + # whose CV score is within 1 stddev of best score + problem.model_selection.CVparameters.oneSE = config["cv_one_stddev"] + problem.model_selection.CVparameters.Nlam = config["lambdas_num_searched"] + problem.model_selection.CVparameters.lamin = config["lambda_min"] + problem.model_selection.CVparameters.logscale = config["lambda_logscale_search"] + + problem.solve() + # todo: try to save output to file + # print(problem.solution) + + # extract coefficients + # if oneSE=True -> uses lambda_1SE else lambda_min + # CV.refit -> solves unconstrained least squares problem with selected + # lambda and variables + alpha = problem.solution.CV.refit + + _report_results_manually_trac(alpha, A_df, log_geom_trainval, y_train_val) + + def train_rf( config: Dict[str, Any], train_val: pd.DataFrame, diff --git a/q2_ritme/process_data.py b/q2_ritme/process_data.py index e8395b6..d7571df 100644 --- a/q2_ritme/process_data.py +++ b/q2_ritme/process_data.py @@ -112,10 +112,11 @@ def load_tax_phylo( # filter tree by feature table: this prunes a phylogenetic tree to match # the input ids # Remove the first letter of each column name: "F" to match phylotree - ft.columns = [col[1:] for col in ft.columns] - art_ft = q2.Artifact.import_data("FeatureTable[RelativeFrequency]", ft) + ft_i = ft.copy() + ft_i.columns = [col[1:] for col in ft_i.columns] + art_ft_i = q2.Artifact.import_data("FeatureTable[RelativeFrequency]", ft_i) - (art_phylo_f,) = phylogeny.actions.filter_tree(tree=art_phylo, table=art_ft) + (art_phylo_f,) = phylogeny.actions.filter_tree(tree=art_phylo, table=art_ft_i) tree_phylo_f = art_phylo_f.view(skbio.TreeNode) # add prefix "F" to leaf names in tree to remain consistent with ft diff --git a/q2_ritme/run_config.json b/q2_ritme/run_config.json index 0691857..616d270 100644 --- a/q2_ritme/run_config.json +++ b/q2_ritme/run_config.json @@ -1,8 +1,8 @@ { - "experiment_tag": "test_5c_trac", + "experiment_tag": "ttrac_5c_trac", "host_id": "host_id", "ls_model_types": [ - "linreg" + "trac" ], "mlflow_tracking_uri": "mlruns", "models_to_evaluate_separately": [], diff --git a/q2_ritme/tune_models.py b/q2_ritme/tune_models.py index 748ca0b..4447b5e 100644 --- a/q2_ritme/tune_models.py +++ b/q2_ritme/tune_models.py @@ -20,6 +20,7 @@ "nn_corn": st.train_nn_corn, "linreg": st.train_linreg, "rf": st.train_rf, + "trac": st.train_trac, } @@ -101,7 +102,7 @@ def run_trials( seed_data=seed_data, seed_model=seed_model, tax=tax, - phylo=tree_phylo, + tree_phylo=tree_phylo, ), resources, ), @@ -157,7 +158,15 @@ def run_all_trials( mlflow_uri: str, path_exp: str, num_trials: int, - model_types: list = ["xgb", "nn_reg", "nn_class", "nn_corn", "linreg", "rf"], + model_types: list = [ + "xgb", + "nn_reg", + "nn_class", + "nn_corn", + "linreg", + "rf", + "trac", + ], fully_reproducible: bool = False, ) -> dict: results_all = {} From 9ab31be3784960a125fbaba37ddd65a9e65ae595 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Fri, 3 May 2024 21:54:02 +0200 Subject: [PATCH 08/28] update trac search space --- q2_ritme/model_space/_static_searchspace.py | 13 +++------- q2_ritme/run_config.json | 27 +++++++++++++++------ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/q2_ritme/model_space/_static_searchspace.py b/q2_ritme/model_space/_static_searchspace.py index 43ea8f2..800b3b0 100644 --- a/q2_ritme/model_space/_static_searchspace.py +++ b/q2_ritme/model_space/_static_searchspace.py @@ -112,16 +112,11 @@ def get_trac_space(train_val): **{ # 'one-cv_one_stddev-error' = select simplest model (largest lambda # value) in CV whose CV score is within 1 stddev of best score - # todo: revert back to full choice - "cv_one_stddev": tune.choice([True]), - # "cv_one_stddev": tune.choice([True, False]), - "lambdas_num_searched": tune.choice([10]), - # "lambdas_num_searched": tune.choice([80, 100, 120]), - "lambda_min": tune.choice([0.01]), - # "lambda_min": tune.choice([0.00001, 0.0001, 0.001, 0.01]), + "cv_one_stddev": tune.choice([True, False]), + "lambdas_num_searched": tune.choice([80, 100, 120]), + "lambda_min": tune.choice([0.00001, 0.0001, 0.001, 0.01]), # logscale when going from lambda_min to 1 - "lambda_logscale_search": tune.choice([True]), - # "lambda_logscale_search": tune.choice([True, False]), + "lambda_logscale_search": tune.choice([True, False]), }, ) diff --git a/q2_ritme/run_config.json b/q2_ritme/run_config.json index 616d270..1f4ca2f 100644 --- a/q2_ritme/run_config.json +++ b/q2_ritme/run_config.json @@ -1,16 +1,27 @@ { - "experiment_tag": "ttrac_5c_trac", + "experiment_tag": "run_config", "host_id": "host_id", "ls_model_types": [ - "trac" + "linreg", + "trac", + "xgb", + "nn_reg", + "nn_class", + "nn_corn", + "rf" ], "mlflow_tracking_uri": "mlruns", - "models_to_evaluate_separately": [], - "num_trials": 1, - "path_to_ft": "experiments/data/220728_monthly/all_otu_table_filt.qza", - "path_to_md": "experiments/data/220728_monthly/metadata_proc_v20240323_r0_r3_le_2yrs.tsv", - "path_to_phylo": "experiments/data/220728_monthly/silva-138-99-rooted-tree.qza", - "path_to_tax": "experiments/data/220728_monthly/otu_taxonomy_all.qza", + "models_to_evaluate_separately": [ + "xgb", + "nn_reg", + "nn_class", + "nn_corn" + ], + "num_trials": 10, + "path_to_ft": "experiments/data/all_otu_table_filt.qza", + "path_to_md": "experiments/data/metadata_proc_v20240323_r0_r3_le_2yrs.tsv", + "path_to_phylo": "experiments/data/silva-138-99-rooted-tree.qza", + "path_to_tax": "experiments/data/otu_taxonomy_all.qza", "seed_data": 12, "seed_model": 12, "target": "age_months", From b605fea42f82cfdec43da5aed77d0a74a41799cd Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Fri, 17 May 2024 11:55:29 +0200 Subject: [PATCH 09/28] 2nd committee meeting setup + best hyperparams range --- README.md | 4 + experiments/import_mlflow.ipynb | 132 ++++++++++++++++++++ q2_ritme/model_space/_static_searchspace.py | 6 +- 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 experiments/import_mlflow.ipynb diff --git a/README.md b/README.md index c23c9ee..04eb449 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ Longitudinal modeling approaches accounting for high-dimensional, sparse and compositional nature of microbial time-series. +## Why q2-ritme? +* q2-ritme allows optimized application of various feature engineering and modelling methods: usually optimal hyperparameters (e.g. regularization) depend on the feature transformation that is performed. q2-ritme can infer feature transformation and optimal model in one go. + + ## Setup To install the required dependencies for this package run (note: running `conda activate` before `make dev` is a mandatory step to ensure also coral_pytorch is installed): ```shell diff --git a/experiments/import_mlflow.ipynb b/experiments/import_mlflow.ipynb new file mode 100644 index 0000000..ca850ac --- /dev/null +++ b/experiments/import_mlflow.ipynb @@ -0,0 +1,132 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import experiments from former MLflow UI\n", + "\n", + "\n", + "this is a workaround if mlflow does not react with too many experiments on Euler" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import mlflow\n", + "import pandas as pd\n", + "\n", + "# Read the CSV file\n", + "df = pd.read_csv(\"models/intermediate_rtrac5c_r0r3_le2y_cpu_t10.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# df.columns.tolist()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "experiment_name = \"euler_imported3\"\n", + "mlflow.create_experiment(experiment_name)\n", + "\n", + "# Get the experiment ID\n", + "experiment = mlflow.get_experiment_by_name(experiment_name)\n", + "experiment_id = experiment.experiment_id\n", + "\n", + "# Define the custom order for the models\n", + "model_order = {\n", + " \"linreg\": 1,\n", + " \"trac\": 2,\n", + " \"xgb\": 3,\n", + " \"nn_reg\": 4,\n", + " \"nn_class\": 5,\n", + " \"nn_corn\": 6,\n", + " \"rf\": 7,\n", + "}\n", + "\n", + "# Iterate over the rows of the DataFrame and log each run\n", + "for _, row in df.iterrows():\n", + " with mlflow.start_run(experiment_id=experiment_id):\n", + " # Log the metrics, parameters, and tags from the CSV\n", + " for key, value in row.items():\n", + " if key.startswith(\"metrics.\"):\n", + " metric_name = key.split(\".\", 1)[1]\n", + " mlflow.log_metric(metric_name, value)\n", + " elif key.startswith(\"params.\"):\n", + " param_name = key.split(\".\", 1)[1]\n", + " mlflow.log_param(param_name, value)\n", + " elif key == \"model\":\n", + " mlflow.log_param(\"model\", value)\n", + " # Log the model order as a separate parameter\n", + " mlflow.log_param(\"model_order\", model_order.get(value, 999))\n", + " elif key == \"time_total_s\":\n", + " mlflow.log_metric(\"time_total_s\", value)\n", + " elif key.startswith(\"tags.\"):\n", + " tag_name = key.split(\".\", 1)[1]\n", + " mlflow.set_tag(tag_name, value)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2024-05-05 13:41:53 +0200] [35348] [INFO] Starting gunicorn 21.2.0\n", + "[2024-05-05 13:41:53 +0200] [35348] [INFO] Listening at: http://127.0.0.1:5004 (35348)\n", + "[2024-05-05 13:41:53 +0200] [35348] [INFO] Using worker: sync\n", + "[2024-05-05 13:41:53 +0200] [35350] [INFO] Booting worker with pid: 35350\n", + "[2024-05-05 13:41:53 +0200] [35352] [INFO] Booting worker with pid: 35352\n", + "[2024-05-05 13:41:53 +0200] [35353] [INFO] Booting worker with pid: 35353\n", + "[2024-05-05 13:41:53 +0200] [35354] [INFO] Booting worker with pid: 35354\n", + "^C\n", + "[2024-05-05 13:44:47 +0200] [35348] [INFO] Handling signal: int\n", + "[2024-05-05 13:44:47 +0200] [35352] [INFO] Worker exiting (pid: 35352)\n", + "[2024-05-05 13:44:47 +0200] [35350] [INFO] Worker exiting (pid: 35350)\n", + "[2024-05-05 13:44:47 +0200] [35354] [INFO] Worker exiting (pid: 35354)\n", + "[2024-05-05 13:44:47 +0200] [35353] [INFO] Worker exiting (pid: 35353)\n" + ] + } + ], + "source": [ + "! mlflow ui --port 5004" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ritme_wclasso_f", + "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.8.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/q2_ritme/model_space/_static_searchspace.py b/q2_ritme/model_space/_static_searchspace.py index 800b3b0..a0b4aa6 100644 --- a/q2_ritme/model_space/_static_searchspace.py +++ b/q2_ritme/model_space/_static_searchspace.py @@ -56,7 +56,7 @@ def get_rf_space(train_val): model="rf", **data_eng_space, **{ - "n_estimators": tune.randint(100, 500), + "n_estimators": tune.randint(50, 300), "max_depth": tune.randint(2, 32), "min_samples_split": tune.choice([0.001, 0.01, 0.1]), "min_samples_leaf": tune.choice([0.0001, 0.001]), @@ -113,8 +113,8 @@ def get_trac_space(train_val): # 'one-cv_one_stddev-error' = select simplest model (largest lambda # value) in CV whose CV score is within 1 stddev of best score "cv_one_stddev": tune.choice([True, False]), - "lambdas_num_searched": tune.choice([80, 100, 120]), - "lambda_min": tune.choice([0.00001, 0.0001, 0.001, 0.01]), + "lambdas_num_searched": tune.choice([60, 80, 100]), + "lambda_min": tune.choice([0.0001, 0.001, 0.01]), # logscale when going from lambda_min to 1 "lambda_logscale_search": tune.choice([True, False]), }, From b35330937a02b1b10d170421394bca7444878dbf Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Tue, 21 May 2024 09:16:21 +0200 Subject: [PATCH 10/28] starting to debug evaluation --- q2_ritme/eval_best_trial_overall.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/q2_ritme/eval_best_trial_overall.py b/q2_ritme/eval_best_trial_overall.py index 1d5d972..9cd1a85 100644 --- a/q2_ritme/eval_best_trial_overall.py +++ b/q2_ritme/eval_best_trial_overall.py @@ -29,7 +29,7 @@ def parse_args(): "--ls_model_types", type=str, nargs="+", - default=["nn_reg", "nn_class", "nn_corn", "xgb", "linreg", "rf"], + default=["nn_reg", "nn_class", "nn_corn", "xgb", "linreg", "rf", "trac"], help="List of model types to evaluate. Separate each model type with a space.", ) return parser.parse_args() @@ -55,11 +55,15 @@ def main(): experiment_dir = f"{model_path}/*/{model}" analyses_ls = get_all_exp_analyses(experiment_dir) - # identify best trial from all analyses of this model type - best_trials_overall[model] = best_trial_name( - analyses_ls, "rmse_val", mode="min" - ) + if len(analyses_ls) == 0: + print(f"No analyses found for model type: {model}") + else: + # identify best trial from all analyses of this model type + best_trials_overall[model] = best_trial_name( + analyses_ls, "rmse_val", mode="min" + ) + print(best_trials_overall) compare_trials(best_trials_overall, model_path, overall_comparison_output) From 91953929f1f2f9775e84fbae3fa814f467261150 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Tue, 21 May 2024 11:29:04 +0200 Subject: [PATCH 11/28] fix empty tax + phylo --- q2_ritme/model_space/_static_trainables.py | 12 ++++++------ q2_ritme/process_data.py | 15 ++++++++------- q2_ritme/tests/test_process_data.py | 10 ++++++++-- q2_ritme/tune_models.py | 9 +++++++++ 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/q2_ritme/model_space/_static_trainables.py b/q2_ritme/model_space/_static_trainables.py index 61db63e..934de93 100644 --- a/q2_ritme/model_space/_static_trainables.py +++ b/q2_ritme/model_space/_static_trainables.py @@ -106,8 +106,8 @@ def train_linreg( host_id: str, seed_data: int, seed_model: int, - tax: pd.DataFrame, - tree_phylo: skbio.TreeNode, + tax: pd.DataFrame = pd.DataFrame(), + tree_phylo: skbio.TreeNode = skbio.TreeNode(), ) -> None: """ Train a linear regression model and report the results to Ray Tune. @@ -249,8 +249,8 @@ def train_rf( host_id: str, seed_data: int, seed_model: int, - tax: pd.DataFrame, - tree_phylo: skbio.TreeNode, + tax: pd.DataFrame = pd.DataFrame(), + tree_phylo: skbio.TreeNode = skbio.TreeNode(), ) -> None: """ Train a random forest model and report the results to Ray Tune. @@ -521,8 +521,8 @@ def train_xgb( host_id: str, seed_data: int, seed_model: int, - tax: pd.DataFrame, - tree_phylo: skbio.TreeNode, + tax: pd.DataFrame = pd.DataFrame(), + tree_phylo: skbio.TreeNode = skbio.TreeNode(), ) -> None: """ Train an XGBoost model and report the results to Ray Tune. diff --git a/q2_ritme/process_data.py b/q2_ritme/process_data.py index d7571df..fa1a89a 100644 --- a/q2_ritme/process_data.py +++ b/q2_ritme/process_data.py @@ -127,9 +127,10 @@ def load_tax_phylo( num_leaves = tree_phylo_f.count(tips=True) assert num_leaves == ft.shape[1] else: - raise ValueError( - "Simulation of taxonomy and phylogeny data not implemented yet." - ) + # load empty variables + df_tax_f = pd.DataFrame() + tree_phylo_f = skbio.TreeNode() + return df_tax_f, tree_phylo_f @@ -215,10 +216,10 @@ def load_n_split_data( is used. path2ft (str, optional): Path to features file. If None, simulated data is used. - path2tax (str, optional): Path to taxonomy file. If None, simulated data - is used. - path2phylo (str, optional): Path to phylogeny file. If None, simulated data - is used. + path2tax (str, optional): Path to taxonomy file. If None, model options + requiring taxonomy can't be run. + path2phylo (str, optional): Path to phylogeny file. If None, model + options requiring taxonomy can't be run. host_id (str, optional): ID of the host. Default is HOST_ID from config. target (str, optional): Name of target variable. Default is TARGET from config. diff --git a/q2_ritme/tests/test_process_data.py b/q2_ritme/tests/test_process_data.py index a61e858..c0b0a4e 100644 --- a/q2_ritme/tests/test_process_data.py +++ b/q2_ritme/tests/test_process_data.py @@ -169,9 +169,11 @@ def test_split_data_by_host_error_one_host(self): def test_load_n_split_data(self): # Call the function with the test paths - train_val, test = load_n_split_data( + train_val, test, tax, tree_phylo = load_n_split_data( self.tmp_md_path, self.tmp_ft_rel_path, + None, + None, host_id="host_id", target="supertarget", train_size=0.8, @@ -179,10 +181,14 @@ def test_load_n_split_data(self): filter_md_cols=["host_id", "supertarget"], ) - # Check that the dataframes are not empty + # Check that the train + test dataframes are not empty self.assertFalse(train_val.empty) self.assertFalse(test.empty) # Check that the dataframes do not overlap overlap = pd.merge(train_val, test, how="inner") self.assertEqual(len(overlap), 0) + + # tax and phylo should be empty in this case + self.assertTrue(tax.empty) + self.assertTrue(tree_phylo.children == []) diff --git a/q2_ritme/tune_models.py b/q2_ritme/tune_models.py index 4447b5e..4037a0b 100644 --- a/q2_ritme/tune_models.py +++ b/q2_ritme/tune_models.py @@ -171,6 +171,15 @@ def run_all_trials( ) -> dict: results_all = {} model_search_space = ss.get_search_space(train_val) + + # if tax + phylogeny empty we can't run trac + if tax.empty or tree_phylo.children == []: + model_types.remove("trac") + print( + "Removing trac from model_types since no taxonomy and phylogeny were " + "provided." + ) + for model in model_types: # todo: parallelize this for loop if not os.path.exists(path_exp): From 5fb4c83b1fa767e94af5f875fe3efaabba1fbe44 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Tue, 21 May 2024 15:50:58 +0200 Subject: [PATCH 12/28] testing individual classo model --- experiments/implement_matrixA.ipynb | 153 ++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 30 deletions(-) diff --git a/experiments/implement_matrixA.ipynb b/experiments/implement_matrixA.ipynb index 58ad790..bc9378f 100644 --- a/experiments/implement_matrixA.ipynb +++ b/experiments/implement_matrixA.ipynb @@ -7,15 +7,14 @@ "outputs": [], "source": [ "import numpy as np\n", - "import os\n", "import pandas as pd\n", "import qiime2 as q2\n", "import skbio\n", - "import pickle\n", - "\n", - "from classo import classo_problem\n", + "from classo import Classo, classo_problem\n", + "from numpy import linalg\n", "from qiime2.plugins import phylogeny\n", "from skbio import TreeNode\n", + "\n", "from q2_ritme.process_data import load_n_split_data\n", "\n", "%matplotlib inline\n", @@ -446,15 +445,15 @@ }, "outputs": [], "source": [ - "# save model: A, label, alpha (includes selected_ft)\n", - "# todo: adjust path\n", - "path2out = \"test_model\"\n", - "if not os.path.exists(path2out):\n", - " os.makedirs(path2out)\n", + "# # save model: A, label, alpha (includes selected_ft)\n", + "# # todo: adjust path\n", + "# path2out = \"test_model\"\n", + "# if not os.path.exists(path2out):\n", + "# os.makedirs(path2out)\n", "\n", - "# storing A w labels\n", - "df_A_with_labels = pd.DataFrame(A, columns=label, index=label[:nb_features])\n", - "df_A_with_labels.to_csv(os.path.join(path2out, \"matrix_a_w_labels.csv\"), index=True)" + "# # storing A w labels\n", + "# df_A_with_labels = pd.DataFrame(A, columns=label, index=label[:nb_features])\n", + "# df_A_with_labels.to_csv(os.path.join(path2out, \"matrix_a_w_labels.csv\"), index=True)" ] }, { @@ -465,18 +464,112 @@ }, "outputs": [], "source": [ - "# storing alpha w labels\n", - "idx_alpha = [\"intercept\"] + df_A_with_labels.columns.tolist()\n", - "df_alpha_with_labels = pd.DataFrame(alpha, columns=[\"alpha\"], index=idx_alpha)\n", - "df_alpha_with_labels.to_csv(\n", - " os.path.join(path2out, \"model_alpha_w_labels.csv\"), index=True\n", + "# # storing alpha w labels\n", + "# idx_alpha = [\"intercept\"] + df_A_with_labels.columns.tolist()\n", + "# df_alpha_with_labels = pd.DataFrame(alpha, columns=[\"alpha\"], index=idx_alpha)\n", + "# df_alpha_with_labels.to_csv(\n", + "# os.path.join(path2out, \"model_alpha_w_labels.csv\"), index=True\n", + "# )\n", + "\n", + "# # we can get selected features from alpha\n", + "# selected_ft_inf = df_alpha_with_labels[\n", + "# df_alpha_with_labels[\"alpha\"] != 0\n", + "# ].index.tolist()\n", + "# assert selected_ft_inf[1:] == selected_ft.tolist()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solve with Classo directly" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# former alpha AFTER refit\n", + "# lam used in code = 0.001191103133283007 = problem.solution.CV.lambda_1SE\n", + "alpha" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lam = problem.solution.CV.lambda_1SE\n", + "\n", + "matrices = (log_geom_trainval, np.ones((1, len(log_geom_trainval[0]))), y_train_val)\n", + "\n", + "method = \"Path-Alg\"\n", + "intercept = True\n", + "beta_norefit = Classo(\n", + " matrix=matrices, lam=lam, typ=\"R1\", meth=method, w=1 / nleaves, intercept=intercept\n", ")\n", + "# new alpha\n", + "beta_norefit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def solve_unpenalized_least_squares(cmatrices, intercept=False):\n", + " # adapted from classo > misc_functions.py > unpenalised\n", + " if intercept:\n", + " A1, C1, y = cmatrices\n", + " A = np.concatenate([np.ones((len(A1), 1)), A1], axis=1)\n", + " C = np.concatenate([np.zeros((len(C1), 1)), C1], axis=1)\n", + " else:\n", + " A, C, y = cmatrices\n", + "\n", + " k = len(C)\n", + " d = len(A[0])\n", + " M1 = np.concatenate([A.T.dot(A), C.T], axis=1)\n", + " M2 = np.concatenate([C, np.zeros((k, k))], axis=1)\n", + " M = np.concatenate([M1, M2], axis=0)\n", + " b = np.concatenate([A.T.dot(y), np.zeros(k)])\n", + " sol = linalg.lstsq(M, b, rcond=None)[0]\n", + " beta = sol[:d]\n", + " return beta\n", + "\n", + "\n", + "def min_least_squares_solution(matrices, selected, intercept=False):\n", + " \"\"\"Minimum Least Squares solution for selected features.\"\"\"\n", + " # adapted from classo > misc_functions.py > min_LS\n", + " X, C, y = matrices\n", + " beta = np.zeros(len(selected))\n", + "\n", + " if intercept:\n", + " beta[selected] = solve_unpenalized_least_squares(\n", + " (X[:, selected[1:]], C[:, selected[1:]], y), intercept=selected[0]\n", + " )\n", + " else:\n", + " beta[selected] = solve_unpenalized_least_squares(\n", + " (X[:, selected], C[:, selected], y), intercept=False\n", + " )\n", + "\n", + " return beta" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "selected_param = abs(beta_norefit) > 1e-5\n", + "beta_refit = min_least_squares_solution(matrices, selected_param, intercept=intercept)\n", + "\n", "\n", - "# we can get selected features from alpha\n", - "selected_ft_inf = df_alpha_with_labels[\n", - " df_alpha_with_labels[\"alpha\"] != 0\n", - "].index.tolist()\n", - "assert selected_ft_inf[1:] == selected_ft.tolist()" + "assert np.array_equal(alpha, beta_refit)" ] }, { @@ -515,13 +608,13 @@ }, "outputs": [], "source": [ - "# test pickle\n", - "# Create a dictionary to store the dataframes\n", - "model = {\"model\": df_alpha_with_labels, \"matrix_a\": df_A_with_labels}\n", + "# # test pickle\n", + "# # Create a dictionary to store the dataframes\n", + "# model = {\"model\": df_alpha_with_labels, \"matrix_a\": df_A_with_labels}\n", "\n", - "# Serialize the dictionary to a pickle file\n", - "with open(\"data.pkl\", \"wb\") as file:\n", - " pickle.dump(model, file)" + "# # Serialize the dictionary to a pickle file\n", + "# with open(\"data.pkl\", \"wb\") as file:\n", + "# pickle.dump(model, file)" ] }, { @@ -532,8 +625,8 @@ }, "outputs": [], "source": [ - "with open(\"data.pkl\", \"rb\") as file:\n", - " model = pickle.load(file)" + "# with open(\"data.pkl\", \"rb\") as file:\n", + "# model = pickle.load(file)" ] } ], From aaaefa43801ff4eb86a7343b3348c319d901bb14 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Tue, 21 May 2024 17:12:19 +0200 Subject: [PATCH 13/28] add trac as individual model no CV --- q2_ritme/feature_space/_process_train.py | 22 +++- q2_ritme/model_space/_static_searchspace.py | 10 +- q2_ritme/model_space/_static_trainables.py | 139 ++++++++++++-------- q2_ritme/tests/test_feature_space.py | 2 +- q2_ritme/tests/test_static_trainables.py | 7 +- 5 files changed, 114 insertions(+), 66 deletions(-) diff --git a/q2_ritme/feature_space/_process_train.py b/q2_ritme/feature_space/_process_train.py index c697e21..981cc93 100644 --- a/q2_ritme/feature_space/_process_train.py +++ b/q2_ritme/feature_space/_process_train.py @@ -57,7 +57,7 @@ def _verify_matrix_a(A, feature_columns, tree_phylo): def _preprocess_taxonomy_aggregation(x, A): pseudo_count = 0.000001 - # ? what happens if x is relative abundances + X = np.log(pseudo_count + x) nleaves = np.sum(A, axis=0) log_geom = X.dot(A) / nleaves @@ -87,7 +87,22 @@ def process_train(config, train_val, target, host_id, seed_data): train, val = split_data_by_host(train_val_t, host_id, 0.8, seed_data) X_train, y_train = train[feature_columns], train[target] X_val, y_val = val[feature_columns], val[target] - return X_train.values, y_train.values, X_val.values, y_val.values + return X_train.values, y_train.values, X_val.values, y_val.values, feature_columns + + +def derive_matrix_a(tree_phylo, tax, feature_columns): + # todo: fix a2_names to be consensus taxonomy names + a, a2_names = _create_matrix_from_tree(tree_phylo) + _verify_matrix_a(a, feature_columns, tree_phylo) + + # get labels for all dimensions of A -> A_df + label = tax["Taxon"].values + nb_features = len(feature_columns) + assert len(label) == len(feature_columns) + label = np.append(label, a2_names) + assert len(label) == a.shape[1] + a_df = pd.DataFrame(a, columns=label, index=label[:nb_features]) + return a_df def process_train_trac(config, train_val, target, host_id, seed_data, tax, tree_phylo): @@ -99,10 +114,11 @@ def process_train_trac(config, train_val, target, host_id, seed_data, tax, tree_ # no need to split train-val for trac since CV is performed within the model # derive matrix A + # todo: fix a2_names to be consensus taxonomy names A, a2_names = _create_matrix_from_tree(tree_phylo) _verify_matrix_a(A, feature_columns, tree_phylo) - # get labels for all dimensions of A + # get labels for all dimensions of A -> A_df label = tax["Taxon"].values nb_features = len(feature_columns) assert len(label) == len(feature_columns) diff --git a/q2_ritme/model_space/_static_searchspace.py b/q2_ritme/model_space/_static_searchspace.py index a0b4aa6..27d4f1a 100644 --- a/q2_ritme/model_space/_static_searchspace.py +++ b/q2_ritme/model_space/_static_searchspace.py @@ -110,13 +110,9 @@ def get_trac_space(train_val): model="trac", **data_eng_space, **{ - # 'one-cv_one_stddev-error' = select simplest model (largest lambda - # value) in CV whose CV score is within 1 stddev of best score - "cv_one_stddev": tune.choice([True, False]), - "lambdas_num_searched": tune.choice([60, 80, 100]), - "lambda_min": tune.choice([0.0001, 0.001, 0.01]), - # logscale when going from lambda_min to 1 - "lambda_logscale_search": tune.choice([True, False]), + # with loguniform: sampled values are more densely concentrated + # towards the lower end of the range + "lambda": tune.loguniform(1e-3, 1.0) }, ) diff --git a/q2_ritme/model_space/_static_trainables.py b/q2_ritme/model_space/_static_trainables.py index 934de93..f3e8f93 100644 --- a/q2_ritme/model_space/_static_trainables.py +++ b/q2_ritme/model_space/_static_trainables.py @@ -12,9 +12,10 @@ import skbio import torch import xgboost as xgb -from classo import classo_problem +from classo import Classo from coral_pytorch.dataset import corn_label_from_logits from coral_pytorch.losses import corn_loss +from numpy import linalg from pytorch_lightning import LightningModule, Trainer, seed_everything from pytorch_lightning.callbacks import ModelCheckpoint from ray import tune @@ -29,7 +30,11 @@ from torch.optim import Adam from torch.utils.data import DataLoader, TensorDataset -from q2_ritme.feature_space._process_train import process_train, process_train_trac +from q2_ritme.feature_space._process_train import ( + _preprocess_taxonomy_aggregation, + derive_matrix_a, + process_train, +) def _predict_rmse(model: BaseEstimator, X: np.ndarray, y: np.ndarray) -> float: @@ -124,7 +129,7 @@ def train_linreg( None """ # ! process dataset: X with features & y with host_id - X_train, y_train, X_val, y_val = process_train( + X_train, y_train, X_val, y_val, ft_col = process_train( config, train_val, target, host_id, seed_data ) @@ -140,7 +145,52 @@ def train_linreg( _report_results_manually(linreg, X_train, y_train, X_val, y_val) -def _report_results_manually_trac(alpha, A_df, log_geom_trainval, y_train_val): +def solve_unpenalized_least_squares(cmatrices, intercept=False): + # adapted from classo > misc_functions.py > unpenalised + if intercept: + A1, C1, y = cmatrices + A = np.concatenate([np.ones((len(A1), 1)), A1], axis=1) + C = np.concatenate([np.zeros((len(C1), 1)), C1], axis=1) + else: + A, C, y = cmatrices + + k = len(C) + d = len(A[0]) + M1 = np.concatenate([A.T.dot(A), C.T], axis=1) + M2 = np.concatenate([C, np.zeros((k, k))], axis=1) + M = np.concatenate([M1, M2], axis=0) + b = np.concatenate([A.T.dot(y), np.zeros(k)]) + sol = linalg.lstsq(M, b, rcond=None)[0] + beta = sol[:d] + return beta + + +def min_least_squares_solution(matrices, selected, intercept=False): + """Minimum Least Squares solution for selected features.""" + # adapted from classo > misc_functions.py > min_LS + X, C, y = matrices + beta = np.zeros(len(selected)) + + if intercept: + beta[selected] = solve_unpenalized_least_squares( + (X[:, selected[1:]], C[:, selected[1:]], y), intercept=selected[0] + ) + else: + beta[selected] = solve_unpenalized_least_squares( + (X[:, selected], C[:, selected], y), intercept=False + ) + + return beta + + +def _predict_rmse_trac(alpha, log_geom_X, y): + y_pred = log_geom_X.dot(alpha[1:]) + alpha[0] + return mean_squared_error(y, y_pred, squared=False) + + +def _report_results_manually_trac( + alpha, A_df, log_geom_train, y_train, log_geom_val, y_val +): # save coefficients w labels & matrix A with labels -> model_path idx_alpha = ["intercept"] + A_df.columns.tolist() df_alpha_with_labels = pd.DataFrame(alpha, columns=["alpha"], index=idx_alpha) @@ -152,19 +202,15 @@ def _report_results_manually_trac(alpha, A_df, log_geom_trainval, y_train_val): model_path = os.path.join(path_to_save, "model.pkl") with open(model_path, "wb") as file: pickle.dump(model, file) - # with pd.HDFStore(model_path, mode="w") as store: - # store["model"] = df_alpha_with_labels - # store["matrix_a"] = A_df # calculate RMSE - y_pred = log_geom_trainval.dot(alpha[1:]) + alpha[0] - score_train_val = mean_squared_error(y_train_val, y_pred, squared=False) + score_train = _predict_rmse_trac(alpha, log_geom_train, y_train) + score_val = _predict_rmse_trac(alpha, log_geom_val, y_val) session.report( metrics={ - # todo: check is this a problem that both are given? - "rmse_val": score_train_val, - "rmse_train": score_train_val, + "rmse_val": score_val, + "rmse_train": score_train, "model_path": model_path, } ) @@ -196,50 +242,37 @@ def train_trac( None """ # ! process dataset: X with features & y with host_id - log_geom_trainval, y_train_val, nleaves, A_df = process_train_trac( - config, train_val, target, host_id, seed_data, tax, tree_phylo + X_train, y_train, X_val, y_val, ft_col = process_train( + config, train_val, target, host_id, seed_data ) + # ! derive matrix A + a_df = derive_matrix_a(tree_phylo, tax, ft_col) + + # ! get log_geom + log_geom_train, nleaves = _preprocess_taxonomy_aggregation(X_train, a_df.values) + log_geom_val, _ = _preprocess_taxonomy_aggregation(X_val, a_df.values) # ! model np.random.seed(seed_model) - - # perform CV classo: trac - label_short = np.array([la.split(";")[-1].strip() for la in A_df.columns]) - problem = classo_problem(log_geom_trainval, y_train_val.values, label=label_short) - - problem.formulation.w = 1 / nleaves - problem.formulation.intercept = True - problem.formulation.concomitant = False # not relevant for here - - # ! one form of model selection needs to be chosen - # stability selection: for pre-selected range of lambda find beta paths - problem.model_selection.StabSel = False - # calculate coefficients for a grid of lambdas - problem.model_selection.PATH = False - # lambda values checked with CV are `Nlam` points between 1 and `lamin`, with - # logarithm scale or not depending on `logscale`. - problem.model_selection.CV = True - problem.model_selection.CVparameters.seed = ( - seed_model # one could change logscale, Nsubset, oneSE + matrices_train = (log_geom_train, np.ones((1, len(log_geom_train[0]))), y_train) + intercept = True + # todo: config["lambda"] = tune.loguniform(1e-4, 1.0) + alpha_norefit = Classo( + matrix=matrices_train, + lam=config["lambda"], + typ="R1", + meth="Path-Alg", + w=1 / nleaves, + intercept=intercept, + ) + selected_param = abs(alpha_norefit) > 1e-5 + alpha = min_least_squares_solution( + matrices_train, selected_param, intercept=intercept ) - # 'one-standard-error' = select simplest model (largest lambda value) in CV - # whose CV score is within 1 stddev of best score - problem.model_selection.CVparameters.oneSE = config["cv_one_stddev"] - problem.model_selection.CVparameters.Nlam = config["lambdas_num_searched"] - problem.model_selection.CVparameters.lamin = config["lambda_min"] - problem.model_selection.CVparameters.logscale = config["lambda_logscale_search"] - - problem.solve() - # todo: try to save output to file - # print(problem.solution) - - # extract coefficients - # if oneSE=True -> uses lambda_1SE else lambda_min - # CV.refit -> solves unconstrained least squares problem with selected - # lambda and variables - alpha = problem.solution.CV.refit - _report_results_manually_trac(alpha, A_df, log_geom_trainval, y_train_val) + _report_results_manually_trac( + alpha, a_df, log_geom_train, y_train, log_geom_val, y_val + ) def train_rf( @@ -267,7 +300,7 @@ def train_rf( None """ # ! process dataset - X_train, y_train, X_val, y_val = process_train( + X_train, y_train, X_val, y_val, ft_col = process_train( config, train_val, target, host_id, seed_data ) @@ -396,7 +429,7 @@ def train_nn( seed_everything(seed_model, workers=True) # Process dataset - X_train, y_train, X_val, y_val = process_train( + X_train, y_train, X_val, y_val, ft_col = process_train( config, train_val, target, host_id, seed_data ) @@ -539,7 +572,7 @@ def train_xgb( None """ # ! process dataset - X_train, y_train, X_val, y_val = process_train( + X_train, y_train, X_val, y_val, ft_col = process_train( config, train_val, target, host_id, seed_data ) # Set seeds diff --git a/q2_ritme/tests/test_feature_space.py b/q2_ritme/tests/test_feature_space.py index 58a8cd7..da6e7b6 100644 --- a/q2_ritme/tests/test_feature_space.py +++ b/q2_ritme/tests/test_feature_space.py @@ -157,7 +157,7 @@ def test_process_train(self, mock_split_data_by_host, mock_transform_features): ) # Act - X_train, y_train, X_val, y_val = process_train( + X_train, y_train, X_val, y_val, ft_col = process_train( self.config, self.train_val, self.target, self.host_id, self.seed_data ) diff --git a/q2_ritme/tests/test_static_trainables.py b/q2_ritme/tests/test_static_trainables.py index 11a7afd..4ad0478 100644 --- a/q2_ritme/tests/test_static_trainables.py +++ b/q2_ritme/tests/test_static_trainables.py @@ -80,7 +80,7 @@ def test_train_linreg(self, mock_report, mock_linreg, mock_process_train): # define input parameters config = {"fit_intercept": True, "alpha": 0.1, "l1_ratio": 0.5} - mock_process_train.return_value = (None, None, None, None) + mock_process_train.return_value = (None, None, None, None, None) mock_linreg_instance = mock_linreg.return_value # run model @@ -112,7 +112,7 @@ def test_train_rf(self, mock_report, mock_rf, mock_process_train): # Arrange config = {"n_estimators": 100, "max_depth": 10} - mock_process_train.return_value = (None, None, None, None) + mock_process_train.return_value = (None, None, None, None, None) mock_rf_instance = mock_rf.return_value # Act @@ -159,11 +159,13 @@ def test_train_xgb( mock_train_y = mock_train[self.target].values mock_test_x = mock_test[["F1", "F2"]].values mock_test_y = mock_test[self.target].values + mock_ft_cols = ["F1", "F2"] mock_process_train.return_value = ( mock_train_x, mock_train_y, mock_test_x, mock_test_y, + mock_ft_cols, ) mock_dmatrix.return_value = None @@ -209,6 +211,7 @@ def test_train_nn( torch.rand(10), torch.rand(3, 5), torch.rand(3), + ["F1", "F2", "F3", "F4", "F5"], ) mock_load_data.return_value = (MagicMock(), MagicMock()) mock_trainer_instance = mock_trainer.return_value From da5c76788ea40392bc14e19f5c7e4d3244a4be43 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 10:58:46 +0200 Subject: [PATCH 14/28] updated env --- ci/recipe/meta.yaml | 7 ++----- q2_ritme/eval_best_trial_overall.py | 1 - q2_ritme/evaluate_models.py | 3 --- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/ci/recipe/meta.yaml b/ci/recipe/meta.yaml index c4d364e..32e7b27 100644 --- a/ci/recipe/meta.yaml +++ b/ci/recipe/meta.yaml @@ -23,18 +23,15 @@ requirements: - qiime2 {{ qiime2_epoch }}.* - q2-feature-table {{ qiime2_epoch }}.* - q2-phylogeny {{ qiime2_epoch }}.* - # todo: check if q2-types is really needed - if not remove - - q2-types {{ qiime2_epoch }}.* - lightning - # todo: once newest version is passing all tests: upgrade mlflow - - mlflow==2.11.3 + - mlflow - numpy - pandas - pip - pytorch - py-xgboost # todo: update ray to newest once Q2 has migrated to Python 3.10 - # note: currently ray is in v2.8.1 + # note: currently ray is in v2.8.1 restricted by Q2 - ray-default - ray-tune - scipy diff --git a/q2_ritme/eval_best_trial_overall.py b/q2_ritme/eval_best_trial_overall.py index 9cd1a85..a11fb63 100644 --- a/q2_ritme/eval_best_trial_overall.py +++ b/q2_ritme/eval_best_trial_overall.py @@ -63,7 +63,6 @@ def main(): analyses_ls, "rmse_val", mode="min" ) - print(best_trials_overall) compare_trials(best_trials_overall, model_path, overall_comparison_output) diff --git a/q2_ritme/evaluate_models.py b/q2_ritme/evaluate_models.py index 5a04cf2..62a1644 100644 --- a/q2_ritme/evaluate_models.py +++ b/q2_ritme/evaluate_models.py @@ -217,7 +217,6 @@ def plot_rmse_over_experiments(preds_dic, save_loc, dpi=400): path_to_save = os.path.join(save_loc, "rmse_over_experiments_train_test.png") plt.tight_layout() plt.savefig(path_to_save, dpi=dpi) - plt.show() def plot_rmse_over_time(preds_dic, ls_model_types, save_loc, dpi=300): @@ -260,7 +259,6 @@ def plot_rmse_over_time(preds_dic, ls_model_types, save_loc, dpi=300): save_loc, f"rmse_over_time_train_test_{model_type}.png" ) plt.savefig(path_to_save, dpi=dpi) - plt.show() def get_best_model_metrics_and_config( @@ -333,7 +331,6 @@ def plot_best_models_comparison( plt.tight_layout() path_to_save = os.path.join(save_loc, "rmse_over_experiments_train_val.png") plt.savefig(path_to_save, dpi=400) - plt.show() def plot_model_training_over_iterations(model_type, result_dic, labels, save_loc): From 7762018af146ed2d064f8a08d928909847cba8bd Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 11:32:27 +0200 Subject: [PATCH 15/28] adding tests --- q2_ritme/feature_space/_process_train.py | 79 +++++++--------------- q2_ritme/model_space/_static_trainables.py | 1 - q2_ritme/tests/test_process_train.py | 56 +++++++++++++++ 3 files changed, 81 insertions(+), 55 deletions(-) create mode 100644 q2_ritme/tests/test_process_train.py diff --git a/q2_ritme/feature_space/_process_train.py b/q2_ritme/feature_space/_process_train.py index 981cc93..8f16793 100644 --- a/q2_ritme/feature_space/_process_train.py +++ b/q2_ritme/feature_space/_process_train.py @@ -5,6 +5,31 @@ from q2_ritme.process_data import split_data_by_host +def _transform_features_in_complete_data(config, train_val, target): + features = [x for x in train_val if x.startswith("F")] + non_features = [x for x in train_val if x not in features] + + ft_transformed = transform_features( + train_val[features], + config["data_transform"], + config["data_alr_denom_idx"], + ) + train_val_t = train_val[non_features].join(ft_transformed) + + return train_val_t, ft_transformed.columns + + +def process_train(config, train_val, target, host_id, seed_data): + train_val_t, feature_columns = _transform_features_in_complete_data( + config, train_val, target + ) + + train, val = split_data_by_host(train_val_t, host_id, 0.8, seed_data) + X_train, y_train = train[feature_columns], train[target] + X_val, y_val = val[feature_columns], val[target] + return X_train.values, y_train.values, X_val.values, y_val.values, feature_columns + + def _create_matrix_from_tree(tree): # Get all leaves and create a mapping from leaf names to indices leaves = list(tree.tips()) @@ -65,31 +90,6 @@ def _preprocess_taxonomy_aggregation(x, A): return log_geom, nleaves -def _transform_features_in_complete_data(config, train_val, target): - features = [x for x in train_val if x.startswith("F")] - non_features = [x for x in train_val if x not in features] - - ft_transformed = transform_features( - train_val[features], - config["data_transform"], - config["data_alr_denom_idx"], - ) - train_val_t = train_val[non_features].join(ft_transformed) - - return train_val_t, ft_transformed.columns - - -def process_train(config, train_val, target, host_id, seed_data): - train_val_t, feature_columns = _transform_features_in_complete_data( - config, train_val, target - ) - - train, val = split_data_by_host(train_val_t, host_id, 0.8, seed_data) - X_train, y_train = train[feature_columns], train[target] - X_val, y_val = val[feature_columns], val[target] - return X_train.values, y_train.values, X_val.values, y_val.values, feature_columns - - def derive_matrix_a(tree_phylo, tax, feature_columns): # todo: fix a2_names to be consensus taxonomy names a, a2_names = _create_matrix_from_tree(tree_phylo) @@ -103,32 +103,3 @@ def derive_matrix_a(tree_phylo, tax, feature_columns): assert len(label) == a.shape[1] a_df = pd.DataFrame(a, columns=label, index=label[:nb_features]) return a_df - - -def process_train_trac(config, train_val, target, host_id, seed_data, tax, tree_phylo): - train_val_t, feature_columns = _transform_features_in_complete_data( - config, train_val, target - ) - X_train_val, y_train_val = train_val_t[feature_columns], train_val_t[target] - - # no need to split train-val for trac since CV is performed within the model - - # derive matrix A - # todo: fix a2_names to be consensus taxonomy names - A, a2_names = _create_matrix_from_tree(tree_phylo) - _verify_matrix_a(A, feature_columns, tree_phylo) - - # get labels for all dimensions of A -> A_df - label = tax["Taxon"].values - nb_features = len(feature_columns) - assert len(label) == len(feature_columns) - label = np.append(label, a2_names) - assert len(label) == A.shape[1] - A_df = pd.DataFrame(A, columns=label, index=label[:nb_features]) - - # get log_geom - log_geom_trainval, nleaves = _preprocess_taxonomy_aggregation( - X_train_val.values, A_df.values - ) - - return log_geom_trainval, y_train_val, nleaves, A_df diff --git a/q2_ritme/model_space/_static_trainables.py b/q2_ritme/model_space/_static_trainables.py index f3e8f93..6c4a339 100644 --- a/q2_ritme/model_space/_static_trainables.py +++ b/q2_ritme/model_space/_static_trainables.py @@ -256,7 +256,6 @@ def train_trac( np.random.seed(seed_model) matrices_train = (log_geom_train, np.ones((1, len(log_geom_train[0]))), y_train) intercept = True - # todo: config["lambda"] = tune.loguniform(1e-4, 1.0) alpha_norefit = Classo( matrix=matrices_train, lam=config["lambda"], diff --git a/q2_ritme/tests/test_process_train.py b/q2_ritme/tests/test_process_train.py new file mode 100644 index 0000000..5164534 --- /dev/null +++ b/q2_ritme/tests/test_process_train.py @@ -0,0 +1,56 @@ +import numpy as np +import pandas as pd +from numpy.testing import assert_array_equal +from qiime2.plugin.testing import TestPluginBase +from skbio import TreeNode + +from q2_ritme.feature_space._process_train import ( + _create_matrix_from_tree, + derive_matrix_a, +) + + +class TestProcessTrain(TestPluginBase): + package = "q2_ritme.test" + + def setUp(self): + super().setUp() + self.tree = self._build_example_tree() + + def _build_example_tree(self): + # Create the tree nodes with lengths + n1 = TreeNode(name="node1") + f1 = TreeNode(name="F1", length=1.0) + f2 = TreeNode(name="F2", length=1.0) + n2 = TreeNode(name="node2") + f3 = TreeNode(name="F3", length=1.0) + + # Build the tree structure with lengths + n1.extend([f1, f2]) + n2.extend([n1, f3]) + n1.length = 1.0 + n2.length = 1.0 + + # n2 is the root of this tree + tree = n2 + + return tree + + def test_create_matrix_from_tree(self): + ma_exp = np.array( + [[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]] + ) + ma_act, a_names_act = _create_matrix_from_tree(self.tree) + + assert_array_equal(ma_exp, ma_act) + self.assertEqual(a_names_act, ["n0"]) + + def test_derive_matrix_a(self): + ft_act = ["F1", "F2", "F3"] + tax_act = ["tax1", "tax2", "tax3"] + tax = pd.DataFrame( + {"Feature ID": ft_act, "Taxon": tax_act, "Confidence": 3 * [0.9]} + ) + a_act = derive_matrix_a(self.tree, tax, ft_act) + + self.assertEqual(a_act.columns.tolist(), tax_act + ["n0"]) From 69a04a23dbe46be7f5426f2c8f23b5c7ba914175 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 13:29:07 +0200 Subject: [PATCH 16/28] try to fix test --- ci/recipe/meta.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/recipe/meta.yaml b/ci/recipe/meta.yaml index 32e7b27..61c13c5 100644 --- a/ci/recipe/meta.yaml +++ b/ci/recipe/meta.yaml @@ -26,6 +26,7 @@ requirements: - lightning - mlflow - numpy + - packaging - pandas - pip - pytorch From e2c36ec93c36e810a6b4bbf010f1fc57a52c018c Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 13:45:48 +0200 Subject: [PATCH 17/28] try to fix test with updated GHAs --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ed1edd..b50994c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,11 +39,11 @@ jobs: run: shell: bash -l {0} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # necessary for versioneer fetch-depth: 0 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: 3.8 - uses: conda-incubator/setup-miniconda@v2 From b51c22f689aea3e749a7d001ab8ed929f8d1f124 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 14:00:44 +0200 Subject: [PATCH 18/28] consistent update of GHA --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b50994c..7e6f90d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up python 3.8 - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: 3.8 From 57326a29fa3938f73113d8851519ffa600ca900e Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 17:00:10 +0200 Subject: [PATCH 19/28] correct node labelling in trac --- experiments/trac_tree_problem.ipynb | 874 +++++++++++++++++++++ q2_ritme/feature_space/_process_train.py | 72 +- q2_ritme/model_space/_static_trainables.py | 4 +- q2_ritme/tests/test_feature_space.py | 4 +- q2_ritme/tests/test_process_data.py | 2 +- q2_ritme/tests/test_process_train.py | 51 +- 6 files changed, 951 insertions(+), 56 deletions(-) create mode 100644 experiments/trac_tree_problem.ipynb diff --git a/experiments/trac_tree_problem.ipynb b/experiments/trac_tree_problem.ipynb new file mode 100644 index 0000000..6f32c43 --- /dev/null +++ b/experiments/trac_tree_problem.ipynb @@ -0,0 +1,874 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Notebook to illustrate problem of weird tree matching to taxonomy" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import qiime2 as q2\n", + "import skbio\n", + "from qiime2.plugins import phylogeny\n", + "\n", + "%matplotlib inline\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Read data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(9478, 5580)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# read feature table\n", + "art_feature_table = q2.Artifact.load(\"data/220728_monthly/all_otu_table_filt.qza\")\n", + "df_ft = art_feature_table.view(pd.DataFrame)\n", + "df_ft.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(5608, 2)\n", + "(5580, 2)\n" + ] + } + ], + "source": [ + "# read taxonomy\n", + "path_to_taxonomy = \"data/220728_monthly/otu_taxonomy_all.qza\"\n", + "art_taxonomy = q2.Artifact.load(path_to_taxonomy)\n", + "df_taxonomy = art_taxonomy.view(pd.DataFrame)\n", + "print(df_taxonomy.shape)\n", + "\n", + "# Filter the taxonomy based on the feature table\n", + "df_taxonomy_f = df_taxonomy[df_taxonomy.index.isin(df_ft.columns.tolist())]\n", + "print(df_taxonomy_f.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "870198\n" + ] + }, + { + "data": { + "text/plain": [ + "11159" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# read silva phylo tree\n", + "path_to_phylo = \"data/220728_monthly/silva-138-99-rooted-tree.qza\"\n", + "art_phylo = q2.Artifact.load(path_to_phylo)\n", + "tree_phylo = art_phylo.view(skbio.TreeNode)\n", + "# total nodes\n", + "print(tree_phylo.count())\n", + "\n", + "# filter tree by feature table: this prunes a phylogenetic tree to match the\n", + "# input ids\n", + "(art_phylo_f,) = phylogeny.actions.filter_tree(tree=art_phylo, table=art_feature_table)\n", + "tree_phylo_f = art_phylo_f.view(skbio.TreeNode)\n", + "\n", + "# total nodes\n", + "tree_phylo_f.count()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# ensure that # leaves in tree == feature table dimension\n", + "num_leaves = tree_phylo_f.count(tips=True)\n", + "assert num_leaves == df_ft.shape[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Get matrix A" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def _create_matrix_from_tree(tree, tax):\n", + " # ! function copied from _process_train.py after git commit \"b51c22f\" in PR\n", + " # ! #16 to have dic_node2leaf output conserved Get all leaves and create a\n", + " # ! mapping from leaf names to indices\n", + " leaves = list(tree.tips())\n", + " leaf_names = [leaf.name for leaf in leaves]\n", + " # map each leaf name to unique index\n", + " leaf_index_map = {name: idx for idx, name in enumerate(leaf_names)}\n", + "\n", + " # Get the number of leaves and internal nodes\n", + " num_leaves = len(leaf_names)\n", + " # root is not included\n", + " internal_nodes = list(tree.non_tips())\n", + "\n", + " # Create the identity matrix for the leaves: A1 (num_leaves x num_leaves)\n", + " A1 = np.eye(num_leaves)\n", + " # taxonomic name should include OTU name\n", + " tax_e = tax.copy()\n", + " tax_e[\"tax_ft\"] = tax_e[\"Taxon\"] + \"; otu__\" + tax_e.index\n", + " a2_node_names = tax_e.loc[leaf_names, \"tax_ft\"].tolist()\n", + " # Create the matrix for the internal nodes: A2 (num_leaves x\n", + " # num_internal_nodes)\n", + " # initialise it with zeros\n", + " A2 = np.zeros((num_leaves, len(internal_nodes)))\n", + "\n", + " # Populate A2 with 1s for the leaves linked by each internal node\n", + " # iterate over all internal nodes to find descendents of this node and mark\n", + " # them accordingly\n", + " dict_node2leaf = {}\n", + " for j, node in enumerate(internal_nodes):\n", + " # per node keep track of leaf names - for consensus naming\n", + " node_leaf_names = []\n", + " # todo: adjust names to consensus taxonomy from descendents\n", + " # for now node names are just increasing integers - since node.name is float\n", + " descendant_leaves = {leaf.name for leaf in node.tips()}\n", + " for leaf_name in leaf_names:\n", + " if leaf_name in descendant_leaves:\n", + " node_leaf_names.append(leaf_name)\n", + " A2[leaf_index_map[leaf_name], j] = 1\n", + "\n", + " # create consensus taxonomy from all leaf_names\n", + " node_mapped_taxon = tax_e.loc[node_leaf_names, \"tax_ft\"].tolist()\n", + " dict_node2leaf[j] = node_mapped_taxon\n", + " str_consensus_taxon = os.path.commonprefix(node_mapped_taxon)\n", + " # get name before last \";\"\n", + " node_consensus_taxon = str_consensus_taxon.rpartition(\";\")[0]\n", + "\n", + " if node_consensus_taxon in a2_node_names:\n", + " # if consensus name already exists, add index to make it unique\n", + " node_consensus_taxon = node_consensus_taxon + \"; n__\" + str(j)\n", + " a2_node_names.append(node_consensus_taxon)\n", + "\n", + " # Concatenate A1 and A2 to create the final matrix A\n", + " A = np.hstack((A1, A2))\n", + "\n", + " return A, a2_node_names, dict_node2leaf" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(5580, 11158)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A, a2_names, dic_node2leaf = _create_matrix_from_tree(tree_phylo_f, df_taxonomy_f)\n", + "\n", + "df_A = pd.DataFrame(A, columns=a2_names)\n", + "df_A.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
d__Archaea; p__Nanoarchaeota; c__Nanoarchaeia; o__Woesearchaeales; f__SCGC_AAA011-D5; g__SCGC_AAA011-D5; s__Nanoarchaeota_archaeon; otu__ASMP01000002.125551.126982d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; otu__JX833581.1.1262d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; s__uncultured_Methanobacteriales; otu__AB535261.1.1262d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanosphaera; otu__CP000102.408655.410144d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanosphaera; s__uncultured_methanogenic; otu__AB905959.1.1268d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; otu__AY196669.1.1262d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; otu__AB905821.1.1267d__Archaea; p__Thermoplasmatota; c__Thermoplasmata; o__Methanomassiliicoccales; f__Methanomethylophilaceae; otu__DQ445723.1.1210d__Archaea; p__Thermoplasmatota; c__Thermoplasmata; o__Methanomassiliicoccales; f__Methanomethylophilaceae; otu__JF980498.1.1419d__Bacteria; p__Patescibacteria; c__Gracilibacteria; o__Absconditabacteriales_(SR1); f__Absconditabacteriales_(SR1); g__Absconditabacteriales_(SR1); s__SR1_bacterium; otu__AOTF01000010.101107.102585...d__Bacteria; n__5568d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; n__5570d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; n__5571d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; n__5572d__Bacteria; p__Chloroflexid__Bacteria; p__Chloroflexi; n__5574d__Bacteria; p__Chloroflexi; n__5575d__Bacteria; p__Chloroflexi; n__5576d__Bacteria; n__5577
01.00.00.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
10.01.00.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
20.00.01.00.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
30.00.00.01.00.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
40.00.00.00.01.00.00.00.00.00.0...0.00.00.00.00.00.00.00.00.00.0
\n", + "

5 rows \u00d7 11158 columns

\n", + "
" + ], + "text/plain": [ + " d__Archaea; p__Nanoarchaeota; c__Nanoarchaeia; o__Woesearchaeales; f__SCGC_AAA011-D5; g__SCGC_AAA011-D5; s__Nanoarchaeota_archaeon; otu__ASMP01000002.125551.126982 \\\n", + "0 1.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; otu__JX833581.1.1262 \\\n", + "0 0.0 \n", + "1 1.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; s__uncultured_Methanobacteriales; otu__AB535261.1.1262 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 1.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanosphaera; otu__CP000102.408655.410144 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 1.0 \n", + "4 0.0 \n", + "\n", + " d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanosphaera; s__uncultured_methanogenic; otu__AB905959.1.1268 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 1.0 \n", + "\n", + " d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; otu__AY196669.1.1262 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Archaea; p__Euryarchaeota; c__Methanobacteria; o__Methanobacteriales; f__Methanobacteriaceae; g__Methanobrevibacter; otu__AB905821.1.1267 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Archaea; p__Thermoplasmatota; c__Thermoplasmata; o__Methanomassiliicoccales; f__Methanomethylophilaceae; otu__DQ445723.1.1210 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Archaea; p__Thermoplasmatota; c__Thermoplasmata; o__Methanomassiliicoccales; f__Methanomethylophilaceae; otu__JF980498.1.1419 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Bacteria; p__Patescibacteria; c__Gracilibacteria; o__Absconditabacteriales_(SR1); f__Absconditabacteriales_(SR1); g__Absconditabacteriales_(SR1); s__SR1_bacterium; otu__AOTF01000010.101107.102585 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " ... d__Bacteria; n__5568 \\\n", + "0 ... 0.0 \n", + "1 ... 0.0 \n", + "2 ... 0.0 \n", + "3 ... 0.0 \n", + "4 ... 0.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; n__5570 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; n__5571 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; n__5572 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi d__Bacteria; p__Chloroflexi; n__5574 \\\n", + "0 0.0 0.0 \n", + "1 0.0 0.0 \n", + "2 0.0 0.0 \n", + "3 0.0 0.0 \n", + "4 0.0 0.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi; n__5575 d__Bacteria; p__Chloroflexi; n__5576 \\\n", + "0 0.0 0.0 \n", + "1 0.0 0.0 \n", + "2 0.0 0.0 \n", + "3 0.0 0.0 \n", + "4 0.0 0.0 \n", + "\n", + " d__Bacteria; n__5577 \n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "\n", + "[5 rows x 11158 columns]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_A.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Show problem with identical \"taxonomic nodes\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
d__Bacteria; p__Chloroflexid__Bacteria; p__Chloroflexi; n__5574d__Bacteria; p__Chloroflexi; n__5575d__Bacteria; p__Chloroflexi; n__5576
00.00.00.00.0
10.00.00.00.0
20.00.00.00.0
30.00.00.00.0
40.00.00.00.0
...............
55751.01.00.01.0
55761.01.00.01.0
55771.01.00.01.0
55780.00.01.01.0
55790.00.01.01.0
\n", + "

5580 rows \u00d7 4 columns

\n", + "
" + ], + "text/plain": [ + " d__Bacteria; p__Chloroflexi d__Bacteria; p__Chloroflexi; n__5574 \\\n", + "0 0.0 0.0 \n", + "1 0.0 0.0 \n", + "2 0.0 0.0 \n", + "3 0.0 0.0 \n", + "4 0.0 0.0 \n", + "... ... ... \n", + "5575 1.0 1.0 \n", + "5576 1.0 1.0 \n", + "5577 1.0 1.0 \n", + "5578 0.0 0.0 \n", + "5579 0.0 0.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi; n__5575 \\\n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "... ... \n", + "5575 0.0 \n", + "5576 0.0 \n", + "5577 0.0 \n", + "5578 1.0 \n", + "5579 1.0 \n", + "\n", + " d__Bacteria; p__Chloroflexi; n__5576 \n", + "0 0.0 \n", + "1 0.0 \n", + "2 0.0 \n", + "3 0.0 \n", + "4 0.0 \n", + "... ... \n", + "5575 1.0 \n", + "5576 1.0 \n", + "5577 1.0 \n", + "5578 1.0 \n", + "5579 1.0 \n", + "\n", + "[5580 rows x 4 columns]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nb_otus = 5580\n", + "\n", + "df_A.iloc[:, nb_otus + 5573 : nb_otus + 5577]" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "d__Bacteria; p__Chloroflexi 6.0\n", + "d__Bacteria; p__Chloroflexi; n__5574 7.0\n", + "d__Bacteria; p__Chloroflexi; n__5575 2.0\n", + "d__Bacteria; p__Chloroflexi; n__5576 9.0\n", + "dtype: float64" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_A.iloc[:, nb_otus + 5573 : nb_otus + 5577].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['d__Bacteria; p__Chloroflexi; c__OLB14; o__OLB14; f__OLB14; g__OLB14; s__uncultured_bacterium; otu__KT835595.1.1453',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__bacterium_QTYC46b; otu__JQ624352.1.1469',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_bacterium; otu__JQ978845.1.1460',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_Chloroflexi; otu__AM935498.1.1337',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_soil; otu__FQ659571.1.1334',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__metagenome; otu__FPLS01036268.22.1504']" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# get matching leave taxonomic names\n", + "dic_node2leaf[5573]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; f__SBR1031; g__SBR1031; s__anaerobic_digester; otu__CZCB01001507.135.1626',\n", + " 'd__Bacteria; p__Chloroflexi; c__OLB14; o__OLB14; f__OLB14; g__OLB14; s__uncultured_bacterium; otu__KT835595.1.1453',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__bacterium_QTYC46b; otu__JQ624352.1.1469',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_bacterium; otu__JQ978845.1.1460',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_Chloroflexi; otu__AM935498.1.1337',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_soil; otu__FQ659571.1.1334',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__metagenome; otu__FPLS01036268.22.1504']" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dic_node2leaf[5574]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['d__Bacteria; p__Chloroflexi; c__Gitt-GS-136; o__Gitt-GS-136; f__Gitt-GS-136; g__Gitt-GS-136; otu__HM299006.1.1326',\n", + " 'd__Bacteria; p__Chloroflexi; c__KD4-96; o__KD4-96; f__KD4-96; g__KD4-96; s__uncultured_bacterium; otu__KY190653.1.1395']" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dic_node2leaf[5575]" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; f__SBR1031; g__SBR1031; s__anaerobic_digester; otu__CZCB01001507.135.1626',\n", + " 'd__Bacteria; p__Chloroflexi; c__OLB14; o__OLB14; f__OLB14; g__OLB14; s__uncultured_bacterium; otu__KT835595.1.1453',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__bacterium_QTYC46b; otu__JQ624352.1.1469',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_bacterium; otu__JQ978845.1.1460',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_Chloroflexi; otu__AM935498.1.1337',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__uncultured_soil; otu__FQ659571.1.1334',\n", + " 'd__Bacteria; p__Chloroflexi; c__Chloroflexia; o__Thermomicrobiales; f__JG30-KF-CM45; g__JG30-KF-CM45; s__metagenome; otu__FPLS01036268.22.1504',\n", + " 'd__Bacteria; p__Chloroflexi; c__Gitt-GS-136; o__Gitt-GS-136; f__Gitt-GS-136; g__Gitt-GS-136; otu__HM299006.1.1326',\n", + " 'd__Bacteria; p__Chloroflexi; c__KD4-96; o__KD4-96; f__KD4-96; g__KD4-96; s__uncultured_bacterium; otu__KY190653.1.1395']" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dic_node2leaf[5576]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Is this a problem?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ritme", + "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.8.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/q2_ritme/feature_space/_process_train.py b/q2_ritme/feature_space/_process_train.py index 8f16793..7265b8f 100644 --- a/q2_ritme/feature_space/_process_train.py +++ b/q2_ritme/feature_space/_process_train.py @@ -1,3 +1,5 @@ +import os + import numpy as np import pandas as pd @@ -30,7 +32,18 @@ def process_train(config, train_val, target, host_id, seed_data): return X_train.values, y_train.values, X_val.values, y_val.values, feature_columns -def _create_matrix_from_tree(tree): +def _verify_matrix_a(A, feature_columns, tree_phylo): + # no all 1 in one column + assert not np.any(np.all(A == 1.0, axis=0)) + + # shape should be = feature_count + node_count + nb_features = len(feature_columns) + nb_non_leaf_nodes = len(list(tree_phylo.non_tips())) + + assert nb_features + nb_non_leaf_nodes == A.shape[1] + + +def create_matrix_from_tree(tree, tax) -> pd.DataFrame: # Get all leaves and create a mapping from leaf names to indices leaves = list(tree.tips()) leaf_names = [leaf.name for leaf in leaves] @@ -44,7 +57,10 @@ def _create_matrix_from_tree(tree): # Create the identity matrix for the leaves: A1 (num_leaves x num_leaves) A1 = np.eye(num_leaves) - + # taxonomic name should include OTU name + tax_e = tax.copy() + tax_e["tax_ft"] = tax_e["Taxon"] + "; otu__" + tax_e.index + a2_node_names = tax_e.loc[leaf_names, "tax_ft"].tolist() # Create the matrix for the internal nodes: A2 (num_leaves x # num_internal_nodes) # initialise it with zeros @@ -53,31 +69,36 @@ def _create_matrix_from_tree(tree): # Populate A2 with 1s for the leaves linked by each internal node # iterate over all internal nodes to find descendents of this node and mark # them accordingly - a2_node_names = [] + # dict_node2leaf = {} for j, node in enumerate(internal_nodes): - # todo: adjust names to consensus taxonomy from descentents - # for now node names are just increasing integers - since node.name is float - a2_node_names.append("n" + str(j)) + # per node keep track of leaf names - for consensus naming + node_leaf_names = [] + + # flag leaves that match to a node descendant_leaves = {leaf.name for leaf in node.tips()} for leaf_name in leaf_names: if leaf_name in descendant_leaves: + node_leaf_names.append(leaf_name) A2[leaf_index_map[leaf_name], j] = 1 - # Concatenate A1 and A2 to create the final matrix A - A = np.hstack((A1, A2)) + # create consensus taxonomy from all leaf_names- since node.name is just float + node_mapped_taxon = tax_e.loc[node_leaf_names, "tax_ft"].tolist() + # dict_node2leaf[j] = node_mapped_taxon + str_consensus_taxon = os.path.commonprefix(node_mapped_taxon) + # get name before last ";" + node_consensus_taxon = str_consensus_taxon.rpartition(";")[0] - return A, a2_node_names + # if consensus name already exists, add index to make it unique + if node_consensus_taxon in a2_node_names: + node_consensus_taxon = node_consensus_taxon + "; n__" + str(j) + a2_node_names.append(node_consensus_taxon) + # Concatenate A1 and A2 to create the final matrix A + A = np.hstack((A1, A2)) + df_a = pd.DataFrame(A, columns=a2_node_names, index=leaf_names) -def _verify_matrix_a(A, feature_columns, tree_phylo): - # no all 1 in one column - assert not np.any(np.all(A == 1.0, axis=0)) - - # shape should be = feature_count + node_count - nb_features = len(feature_columns) - nb_non_leaf_nodes = len(list(tree_phylo.non_tips())) - - assert nb_features + nb_non_leaf_nodes == A.shape[1] + _verify_matrix_a(df_a.values, tax.index.tolist(), tree) + return df_a def _preprocess_taxonomy_aggregation(x, A): @@ -88,18 +109,3 @@ def _preprocess_taxonomy_aggregation(x, A): log_geom = X.dot(A) / nleaves return log_geom, nleaves - - -def derive_matrix_a(tree_phylo, tax, feature_columns): - # todo: fix a2_names to be consensus taxonomy names - a, a2_names = _create_matrix_from_tree(tree_phylo) - _verify_matrix_a(a, feature_columns, tree_phylo) - - # get labels for all dimensions of A -> A_df - label = tax["Taxon"].values - nb_features = len(feature_columns) - assert len(label) == len(feature_columns) - label = np.append(label, a2_names) - assert len(label) == a.shape[1] - a_df = pd.DataFrame(a, columns=label, index=label[:nb_features]) - return a_df diff --git a/q2_ritme/model_space/_static_trainables.py b/q2_ritme/model_space/_static_trainables.py index 6c4a339..2d0adc7 100644 --- a/q2_ritme/model_space/_static_trainables.py +++ b/q2_ritme/model_space/_static_trainables.py @@ -32,7 +32,7 @@ from q2_ritme.feature_space._process_train import ( _preprocess_taxonomy_aggregation, - derive_matrix_a, + create_matrix_from_tree, process_train, ) @@ -246,7 +246,7 @@ def train_trac( config, train_val, target, host_id, seed_data ) # ! derive matrix A - a_df = derive_matrix_a(tree_phylo, tax, ft_col) + a_df = create_matrix_from_tree(tree_phylo, tax) # ! get log_geom log_geom_train, nleaves = _preprocess_taxonomy_aggregation(X_train, a_df.values) diff --git a/q2_ritme/tests/test_feature_space.py b/q2_ritme/tests/test_feature_space.py index da6e7b6..cd3b518 100644 --- a/q2_ritme/tests/test_feature_space.py +++ b/q2_ritme/tests/test_feature_space.py @@ -16,7 +16,7 @@ class TestTransformFeatures(TestPluginBase): - package = "q2_ritme.test" + package = "q2_ritme.tests" def setUp(self): super().setUp() @@ -119,7 +119,7 @@ def test_transform_features_error(self): class TestProcessTrain(TestPluginBase): - package = "q2_ritme.test" + package = "q2_ritme.tests" def setUp(self): super().setUp() diff --git a/q2_ritme/tests/test_process_data.py b/q2_ritme/tests/test_process_data.py index c0b0a4e..97ceaea 100644 --- a/q2_ritme/tests/test_process_data.py +++ b/q2_ritme/tests/test_process_data.py @@ -16,7 +16,7 @@ class TestProcessData(TestPluginBase): - package = "q2_ritme.test" + package = "q2_ritme.tests" def setUp(self): super().setUp() diff --git a/q2_ritme/tests/test_process_train.py b/q2_ritme/tests/test_process_train.py index 5164534..e50f4ef 100644 --- a/q2_ritme/tests/test_process_train.py +++ b/q2_ritme/tests/test_process_train.py @@ -1,21 +1,36 @@ import numpy as np import pandas as pd -from numpy.testing import assert_array_equal +from pandas.testing import assert_frame_equal from qiime2.plugin.testing import TestPluginBase from skbio import TreeNode -from q2_ritme.feature_space._process_train import ( - _create_matrix_from_tree, - derive_matrix_a, -) +from q2_ritme.feature_space._process_train import create_matrix_from_tree class TestProcessTrain(TestPluginBase): - package = "q2_ritme.test" + package = "q2_ritme.tests" def setUp(self): super().setUp() self.tree = self._build_example_tree() + self.tax = self._build_example_taxonomy() + + def _build_example_taxonomy(self): + tax = pd.DataFrame( + { + "Taxon": [ + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031; s__anaerobic_digester", + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031; s__uncultured_bacterium", + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031", + ], + "Confidence": [0.9, 0.9, 0.9], + } + ) + tax.index = ["F1", "F2", "F3"] + tax.index.name = "Feature ID" + return tax def _build_example_tree(self): # Create the tree nodes with lengths @@ -40,17 +55,17 @@ def test_create_matrix_from_tree(self): ma_exp = np.array( [[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]] ) - ma_act, a_names_act = _create_matrix_from_tree(self.tree) - - assert_array_equal(ma_exp, ma_act) - self.assertEqual(a_names_act, ["n0"]) - - def test_derive_matrix_a(self): - ft_act = ["F1", "F2", "F3"] - tax_act = ["tax1", "tax2", "tax3"] - tax = pd.DataFrame( - {"Feature ID": ft_act, "Taxon": tax_act, "Confidence": 3 * [0.9]} + node_taxon_names = [ + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031" + ] + leaf_names = (self.tax["Taxon"] + "; otu__" + self.tax.index).values.tolist() + ft_names = ["F1", "F2", "F3"] + ma_exp = pd.DataFrame( + ma_exp, + columns=leaf_names + node_taxon_names, + index=ft_names, ) - a_act = derive_matrix_a(self.tree, tax, ft_act) + ma_act = create_matrix_from_tree(self.tree, self.tax) - self.assertEqual(a_act.columns.tolist(), tax_act + ["n0"]) + assert_frame_equal(ma_exp, ma_act) From 5a1a221eb55bb03d4420d4ddc3ab630be55744f7 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 17:08:25 +0200 Subject: [PATCH 20/28] try to fix GHA --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e6f90d..ae6c52b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 @@ -43,7 +43,7 @@ jobs: with: # necessary for versioneer fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.8 - uses: conda-incubator/setup-miniconda@v2 From cb742ce1df10415a70f7a772a916ab7a5716b671 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 17:37:47 +0200 Subject: [PATCH 21/28] fix node naming in df_a --- q2_ritme/evaluate_models.py | 5 ----- q2_ritme/feature_space/_process_train.py | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/q2_ritme/evaluate_models.py b/q2_ritme/evaluate_models.py index 62a1644..bb73ea0 100644 --- a/q2_ritme/evaluate_models.py +++ b/q2_ritme/evaluate_models.py @@ -57,11 +57,6 @@ def load_trac_model(result: Result) -> Any: :param result: The result object containing the model path. :return: The loaded TRAC model. """ - # with pd.HDFStore(result.metrics["model_path"], mode="r") as store: - # alpha_df = store["model"] - # A_df = store["matrix_a"] - # model = {"model": alpha_df, "matrix_a": A_df} - with open(result.metrics["model_path"], "rb") as file: model = pickle.load(file) diff --git a/q2_ritme/feature_space/_process_train.py b/q2_ritme/feature_space/_process_train.py index 7265b8f..b8d6cc9 100644 --- a/q2_ritme/feature_space/_process_train.py +++ b/q2_ritme/feature_space/_process_train.py @@ -60,7 +60,7 @@ def create_matrix_from_tree(tree, tax) -> pd.DataFrame: # taxonomic name should include OTU name tax_e = tax.copy() tax_e["tax_ft"] = tax_e["Taxon"] + "; otu__" + tax_e.index - a2_node_names = tax_e.loc[leaf_names, "tax_ft"].tolist() + a1_node_names = tax_e.loc[leaf_names, "tax_ft"].tolist() # Create the matrix for the internal nodes: A2 (num_leaves x # num_internal_nodes) # initialise it with zeros @@ -70,6 +70,7 @@ def create_matrix_from_tree(tree, tax) -> pd.DataFrame: # iterate over all internal nodes to find descendents of this node and mark # them accordingly # dict_node2leaf = {} + a2_node_names = [] for j, node in enumerate(internal_nodes): # per node keep track of leaf names - for consensus naming node_leaf_names = [] @@ -95,7 +96,7 @@ def create_matrix_from_tree(tree, tax) -> pd.DataFrame: # Concatenate A1 and A2 to create the final matrix A A = np.hstack((A1, A2)) - df_a = pd.DataFrame(A, columns=a2_node_names, index=leaf_names) + df_a = pd.DataFrame(A, columns=a1_node_names + a2_node_names, index=leaf_names) _verify_matrix_a(df_a.values, tax.index.tolist(), tree) return df_a @@ -106,6 +107,8 @@ def _preprocess_taxonomy_aggregation(x, A): X = np.log(pseudo_count + x) nleaves = np.sum(A, axis=0) + # safekeeping: dot-product would not work with wrong dimensions + # X: n_samples, n_features, A: n_features, (n_features+n_nodes) log_geom = X.dot(A) / nleaves return log_geom, nleaves From 5fdd54b2e9fa820c7ecd9e5ef2749ade19b08c31 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 18:17:42 +0200 Subject: [PATCH 22/28] modularize trac model --- .../feature_space/_process_trac_specific.py | 116 ++++++++++++++++++ q2_ritme/feature_space/_process_train.py | 87 ------------- q2_ritme/model_space/_model_trac_calc.py | 40 ++++++ q2_ritme/tests/test_feature_space.py | 112 +++++++++++++++++ q2_ritme/tests/test_process_train.py | 71 ----------- 5 files changed, 268 insertions(+), 158 deletions(-) create mode 100644 q2_ritme/feature_space/_process_trac_specific.py create mode 100644 q2_ritme/model_space/_model_trac_calc.py delete mode 100644 q2_ritme/tests/test_process_train.py diff --git a/q2_ritme/feature_space/_process_trac_specific.py b/q2_ritme/feature_space/_process_trac_specific.py new file mode 100644 index 0000000..fc6c55f --- /dev/null +++ b/q2_ritme/feature_space/_process_trac_specific.py @@ -0,0 +1,116 @@ +import os + +import numpy as np +import pandas as pd + + +def _verify_matrix_a(A, feature_columns, tree_phylo): + # no all 1 in one column + assert not np.any(np.all(A == 1.0, axis=0)) + + # shape should be = feature_count + node_count + nb_features = len(feature_columns) + nb_non_leaf_nodes = len(list(tree_phylo.non_tips())) + + assert nb_features + nb_non_leaf_nodes == A.shape[1] + + +def _get_leaves_and_index_map(tree): + leaves = list(tree.tips()) + leaf_names = [leaf.name for leaf in leaves] + # map each leaf name to unique index + leaf_index_map = {name: idx for idx, name in enumerate(leaf_names)} + return leaves, leaf_index_map + + +def _get_internal_nodes(tree): + # root is not included + return list(tree.non_tips()) + + +def _create_identity_matrix_for_leaves(num_leaves, tax, leaves): + A1 = np.eye(num_leaves) + # taxonomic name should include OTU name + tax_e = tax.copy() + tax_e["tax_ft"] = tax_e["Taxon"] + "; otu__" + tax_e.index + a1_node_names = tax_e.loc[[leaf.name for leaf in leaves], "tax_ft"].tolist() + return A1, a1_node_names + + +def _populate_A2_for_node(A2, node, leaf_index_map, j): + node_leaf_names = [] + # flag leaves that match to a node + descendant_leaves = {leaf.name for leaf in node.tips()} + for leaf_name in leaf_index_map: + if leaf_name in descendant_leaves: + node_leaf_names.append(leaf_name) + A2[leaf_index_map[leaf_name], j] = 1 + return A2, node_leaf_names + + +def _create_consensus_taxonomy(node_leaf_names, tax, a2_node_names, j): + tax_e = tax.copy() + tax_e["tax_ft"] = tax_e["Taxon"] + "; otu__" + tax_e.index + node_mapped_taxon = tax_e.loc[node_leaf_names, "tax_ft"].tolist() + str_consensus_taxon = os.path.commonprefix(node_mapped_taxon) + # get name before last ";" + node_consensus_taxon = str_consensus_taxon.rpartition(";")[0] + # if consensus name already exists, add index to make it unique + if node_consensus_taxon in a2_node_names: + node_consensus_taxon = node_consensus_taxon + "; n__" + str(j) + return node_consensus_taxon + + +def _create_matrix_for_internal_nodes(num_leaves, internal_nodes, leaf_index_map, tax): + # initialise it with zeros + A2 = np.zeros((num_leaves, len(internal_nodes))) + a2_node_names = [] + # Populate A2 with 1s for the leaves linked by each internal node # iterate + # over all internal nodes to find descendents of this node and mark them + # accordingly + for j, node in enumerate(internal_nodes): + A2, node_leaf_names = _populate_A2_for_node(A2, node, leaf_index_map, j) + # create consensus taxonomy from all leaf_names- since node.name is just float + node_consensus_taxon = _create_consensus_taxonomy( + node_leaf_names, tax, a2_node_names, j + ) + a2_node_names.append(node_consensus_taxon) + return A2, a2_node_names + + +def create_matrix_from_tree(tree, tax) -> pd.DataFrame: + # Get all leaves and create a mapping from leaf names to indices + leaves, leaf_index_map = _get_leaves_and_index_map(tree) + num_leaves = len(leaves) + + # Get all internal nodes + internal_nodes = _get_internal_nodes(tree) + + # Create the identity matrix for the leaves: A1 (num_leaves x num_leaves) + A1, a1_node_names = _create_identity_matrix_for_leaves(num_leaves, tax, leaves) + + # Create the matrix for the internal nodes: A2 (num_leaves x num_internal_nodes) + A2, a2_node_names = _create_matrix_for_internal_nodes( + num_leaves, internal_nodes, leaf_index_map, tax + ) + + # Concatenate A1 and A2 to create the final matrix A + A = np.hstack((A1, A2)) + df_a = pd.DataFrame( + A, columns=a1_node_names + a2_node_names, index=[leaf.name for leaf in leaves] + ) + _verify_matrix_a(df_a.values, tax.index.tolist(), tree) + + return df_a + + +def _preprocess_taxonomy_aggregation(x, A): + pseudo_count = 0.000001 + + X = np.log(pseudo_count + x) + nleaves = np.sum(A, axis=0) + # safekeeping: dot-product would not work with wrong dimensions + # X: n_samples, n_features, A: n_features, (n_features+n_nodes) + log_geom = X.dot(A) / nleaves + + return log_geom, nleaves diff --git a/q2_ritme/feature_space/_process_train.py b/q2_ritme/feature_space/_process_train.py index b8d6cc9..64128dc 100644 --- a/q2_ritme/feature_space/_process_train.py +++ b/q2_ritme/feature_space/_process_train.py @@ -1,8 +1,3 @@ -import os - -import numpy as np -import pandas as pd - from q2_ritme.feature_space.transform_features import transform_features from q2_ritme.process_data import split_data_by_host @@ -30,85 +25,3 @@ def process_train(config, train_val, target, host_id, seed_data): X_train, y_train = train[feature_columns], train[target] X_val, y_val = val[feature_columns], val[target] return X_train.values, y_train.values, X_val.values, y_val.values, feature_columns - - -def _verify_matrix_a(A, feature_columns, tree_phylo): - # no all 1 in one column - assert not np.any(np.all(A == 1.0, axis=0)) - - # shape should be = feature_count + node_count - nb_features = len(feature_columns) - nb_non_leaf_nodes = len(list(tree_phylo.non_tips())) - - assert nb_features + nb_non_leaf_nodes == A.shape[1] - - -def create_matrix_from_tree(tree, tax) -> pd.DataFrame: - # Get all leaves and create a mapping from leaf names to indices - leaves = list(tree.tips()) - leaf_names = [leaf.name for leaf in leaves] - # map each leaf name to unique index - leaf_index_map = {name: idx for idx, name in enumerate(leaf_names)} - - # Get the number of leaves and internal nodes - num_leaves = len(leaf_names) - # root is not included - internal_nodes = list(tree.non_tips()) - - # Create the identity matrix for the leaves: A1 (num_leaves x num_leaves) - A1 = np.eye(num_leaves) - # taxonomic name should include OTU name - tax_e = tax.copy() - tax_e["tax_ft"] = tax_e["Taxon"] + "; otu__" + tax_e.index - a1_node_names = tax_e.loc[leaf_names, "tax_ft"].tolist() - # Create the matrix for the internal nodes: A2 (num_leaves x - # num_internal_nodes) - # initialise it with zeros - A2 = np.zeros((num_leaves, len(internal_nodes))) - - # Populate A2 with 1s for the leaves linked by each internal node - # iterate over all internal nodes to find descendents of this node and mark - # them accordingly - # dict_node2leaf = {} - a2_node_names = [] - for j, node in enumerate(internal_nodes): - # per node keep track of leaf names - for consensus naming - node_leaf_names = [] - - # flag leaves that match to a node - descendant_leaves = {leaf.name for leaf in node.tips()} - for leaf_name in leaf_names: - if leaf_name in descendant_leaves: - node_leaf_names.append(leaf_name) - A2[leaf_index_map[leaf_name], j] = 1 - - # create consensus taxonomy from all leaf_names- since node.name is just float - node_mapped_taxon = tax_e.loc[node_leaf_names, "tax_ft"].tolist() - # dict_node2leaf[j] = node_mapped_taxon - str_consensus_taxon = os.path.commonprefix(node_mapped_taxon) - # get name before last ";" - node_consensus_taxon = str_consensus_taxon.rpartition(";")[0] - - # if consensus name already exists, add index to make it unique - if node_consensus_taxon in a2_node_names: - node_consensus_taxon = node_consensus_taxon + "; n__" + str(j) - a2_node_names.append(node_consensus_taxon) - - # Concatenate A1 and A2 to create the final matrix A - A = np.hstack((A1, A2)) - df_a = pd.DataFrame(A, columns=a1_node_names + a2_node_names, index=leaf_names) - - _verify_matrix_a(df_a.values, tax.index.tolist(), tree) - return df_a - - -def _preprocess_taxonomy_aggregation(x, A): - pseudo_count = 0.000001 - - X = np.log(pseudo_count + x) - nleaves = np.sum(A, axis=0) - # safekeeping: dot-product would not work with wrong dimensions - # X: n_samples, n_features, A: n_features, (n_features+n_nodes) - log_geom = X.dot(A) / nleaves - - return log_geom, nleaves diff --git a/q2_ritme/model_space/_model_trac_calc.py b/q2_ritme/model_space/_model_trac_calc.py new file mode 100644 index 0000000..8e20665 --- /dev/null +++ b/q2_ritme/model_space/_model_trac_calc.py @@ -0,0 +1,40 @@ +import numpy as np +from numpy import linalg + + +def solve_unpenalized_least_squares(cmatrices, intercept=False): + # adapted from classo > misc_functions.py > unpenalised + if intercept: + A1, C1, y = cmatrices + A = np.concatenate([np.ones((len(A1), 1)), A1], axis=1) + C = np.concatenate([np.zeros((len(C1), 1)), C1], axis=1) + else: + A, C, y = cmatrices + + k = len(C) + d = len(A[0]) + M1 = np.concatenate([A.T.dot(A), C.T], axis=1) + M2 = np.concatenate([C, np.zeros((k, k))], axis=1) + M = np.concatenate([M1, M2], axis=0) + b = np.concatenate([A.T.dot(y), np.zeros(k)]) + sol = linalg.lstsq(M, b, rcond=None)[0] + beta = sol[:d] + return beta + + +def min_least_squares_solution(matrices, selected, intercept=False): + """Minimum Least Squares solution for selected features.""" + # adapted from classo > misc_functions.py > min_LS + X, C, y = matrices + beta = np.zeros(len(selected)) + + if intercept: + beta[selected] = solve_unpenalized_least_squares( + (X[:, selected[1:]], C[:, selected[1:]], y), intercept=selected[0] + ) + else: + beta[selected] = solve_unpenalized_least_squares( + (X[:, selected], C[:, selected], y), intercept=False + ) + + return beta diff --git a/q2_ritme/tests/test_feature_space.py b/q2_ritme/tests/test_feature_space.py index cd3b518..0b12576 100644 --- a/q2_ritme/tests/test_feature_space.py +++ b/q2_ritme/tests/test_feature_space.py @@ -5,8 +5,16 @@ from pandas.testing import assert_frame_equal from qiime2.plugin.testing import TestPluginBase from scipy.stats.mstats import gmean +from skbio import TreeNode from skbio.stats.composition import ilr +from q2_ritme.feature_space._process_trac_specific import ( + _create_identity_matrix_for_leaves, + _create_matrix_for_internal_nodes, + _get_internal_nodes, + _get_leaves_and_index_map, + create_matrix_from_tree, +) from q2_ritme.feature_space._process_train import process_train from q2_ritme.feature_space.transform_features import ( PSEUDOCOUNT, @@ -170,3 +178,107 @@ def test_process_train(self, mock_split_data_by_host, mock_transform_features): 0.8, 0, ) + + +class TestProcessTracSpecific(TestPluginBase): + package = "q2_ritme.tests" + + def setUp(self): + super().setUp() + self.tree = self._build_example_tree() + self.tax = self._build_example_taxonomy() + + def _build_example_taxonomy(self): + tax = pd.DataFrame( + { + "Taxon": [ + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031; s__anaerobic_digester", + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031; s__uncultured_bacterium", + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031", + ], + "Confidence": [0.9, 0.9, 0.9], + } + ) + tax.index = ["F1", "F2", "F3"] + tax.index.name = "Feature ID" + return tax + + def _build_example_tree(self): + # Create the tree nodes with lengths + n1 = TreeNode(name="node1") + f1 = TreeNode(name="F1", length=1.0) + f2 = TreeNode(name="F2", length=1.0) + n2 = TreeNode(name="node2") + f3 = TreeNode(name="F3", length=1.0) + + # Build the tree structure with lengths + n1.extend([f1, f2]) + n2.extend([n1, f3]) + n1.length = 1.0 + n2.length = 1.0 + + # n2 is the root of this tree + tree = n2 + + return tree + + def test_get_leaves_and_index_map(self): + leaves, leaf_index_map = _get_leaves_and_index_map(self.tree) + self.assertEqual(len(leaves), 3) + self.assertEqual(leaf_index_map, {"F1": 0, "F2": 1, "F3": 2}) + + def test_get_internal_nodes(self): + internal_nodes = _get_internal_nodes(self.tree) + self.assertEqual(len(internal_nodes), 1) + self.assertEqual(internal_nodes[0].name, "node1") + + def test_create_identity_matrix_for_leaves(self): + leaves = list(self.tree.tips()) + A1, a1_node_names = _create_identity_matrix_for_leaves(3, self.tax, leaves) + np.testing.assert_array_equal(A1, np.eye(3)) + self.assertEqual( + a1_node_names, + [ + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031; s__anaerobic_digester; otu__F1", + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031; s__uncultured_bacterium; otu__F2", + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; otu__F3", + ], + ) + + def test_create_matrix_for_internal_nodes(self): + leaves, leaf_index_map = _get_leaves_and_index_map(self.tree) + internal_nodes = _get_internal_nodes(self.tree) + A2, a2_node_names = _create_matrix_for_internal_nodes( + 3, internal_nodes, leaf_index_map, self.tax + ) + np.testing.assert_array_equal(A2, np.array([[1], [1], [0]])) + self.assertEqual( + a2_node_names, + [ + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031" + ], + ) + + def test_create_matrix_from_tree(self): + ma_exp = np.array( + [[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]] + ) + node_taxon_names = [ + "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " + "f__SBR1031; g__SBR1031" + ] + leaf_names = (self.tax["Taxon"] + "; otu__" + self.tax.index).values.tolist() + ft_names = ["F1", "F2", "F3"] + ma_exp = pd.DataFrame( + ma_exp, + columns=leaf_names + node_taxon_names, + index=ft_names, + ) + ma_act = create_matrix_from_tree(self.tree, self.tax) + + assert_frame_equal(ma_exp, ma_act) diff --git a/q2_ritme/tests/test_process_train.py b/q2_ritme/tests/test_process_train.py deleted file mode 100644 index e50f4ef..0000000 --- a/q2_ritme/tests/test_process_train.py +++ /dev/null @@ -1,71 +0,0 @@ -import numpy as np -import pandas as pd -from pandas.testing import assert_frame_equal -from qiime2.plugin.testing import TestPluginBase -from skbio import TreeNode - -from q2_ritme.feature_space._process_train import create_matrix_from_tree - - -class TestProcessTrain(TestPluginBase): - package = "q2_ritme.tests" - - def setUp(self): - super().setUp() - self.tree = self._build_example_tree() - self.tax = self._build_example_taxonomy() - - def _build_example_taxonomy(self): - tax = pd.DataFrame( - { - "Taxon": [ - "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " - "f__SBR1031; g__SBR1031; s__anaerobic_digester", - "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " - "f__SBR1031; g__SBR1031; s__uncultured_bacterium", - "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031", - ], - "Confidence": [0.9, 0.9, 0.9], - } - ) - tax.index = ["F1", "F2", "F3"] - tax.index.name = "Feature ID" - return tax - - def _build_example_tree(self): - # Create the tree nodes with lengths - n1 = TreeNode(name="node1") - f1 = TreeNode(name="F1", length=1.0) - f2 = TreeNode(name="F2", length=1.0) - n2 = TreeNode(name="node2") - f3 = TreeNode(name="F3", length=1.0) - - # Build the tree structure with lengths - n1.extend([f1, f2]) - n2.extend([n1, f3]) - n1.length = 1.0 - n2.length = 1.0 - - # n2 is the root of this tree - tree = n2 - - return tree - - def test_create_matrix_from_tree(self): - ma_exp = np.array( - [[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]] - ) - node_taxon_names = [ - "d__Bacteria; p__Chloroflexi; c__Anaerolineae; o__SBR1031; " - "f__SBR1031; g__SBR1031" - ] - leaf_names = (self.tax["Taxon"] + "; otu__" + self.tax.index).values.tolist() - ft_names = ["F1", "F2", "F3"] - ma_exp = pd.DataFrame( - ma_exp, - columns=leaf_names + node_taxon_names, - index=ft_names, - ) - ma_act = create_matrix_from_tree(self.tree, self.tax) - - assert_frame_equal(ma_exp, ma_act) From 4dabf2d8cd51e064b5e741e3bc7f242e1efdb2db Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 18:18:10 +0200 Subject: [PATCH 23/28] rename model_space --- q2_ritme/evaluate_models.py | 2 +- ...c_searchspace.py => static_searchspace.py} | 0 ...tic_trainables.py => static_trainables.py} | 44 ++----------------- q2_ritme/tests/test_static_searchspace.py | 2 +- q2_ritme/tests/test_static_trainables.py | 32 +++++++------- q2_ritme/tune_models.py | 4 +- 6 files changed, 23 insertions(+), 61 deletions(-) rename q2_ritme/model_space/{_static_searchspace.py => static_searchspace.py} (100%) rename q2_ritme/model_space/{_static_trainables.py => static_trainables.py} (92%) diff --git a/q2_ritme/evaluate_models.py b/q2_ritme/evaluate_models.py index bb73ea0..5505d3c 100644 --- a/q2_ritme/evaluate_models.py +++ b/q2_ritme/evaluate_models.py @@ -14,7 +14,7 @@ from q2_ritme.feature_space._process_train import _preprocess_taxonomy_aggregation from q2_ritme.feature_space.transform_features import transform_features -from q2_ritme.model_space._static_trainables import NeuralNet +from q2_ritme.model_space.static_trainables import NeuralNet plt.rcParams.update({"font.family": "DejaVu Sans"}) plt.style.use("seaborn-v0_8-pastel") diff --git a/q2_ritme/model_space/_static_searchspace.py b/q2_ritme/model_space/static_searchspace.py similarity index 100% rename from q2_ritme/model_space/_static_searchspace.py rename to q2_ritme/model_space/static_searchspace.py diff --git a/q2_ritme/model_space/_static_trainables.py b/q2_ritme/model_space/static_trainables.py similarity index 92% rename from q2_ritme/model_space/_static_trainables.py rename to q2_ritme/model_space/static_trainables.py index 2d0adc7..f3c7d0a 100644 --- a/q2_ritme/model_space/_static_trainables.py +++ b/q2_ritme/model_space/static_trainables.py @@ -15,7 +15,6 @@ from classo import Classo from coral_pytorch.dataset import corn_label_from_logits from coral_pytorch.losses import corn_loss -from numpy import linalg from pytorch_lightning import LightningModule, Trainer, seed_everything from pytorch_lightning.callbacks import ModelCheckpoint from ray import tune @@ -30,11 +29,12 @@ from torch.optim import Adam from torch.utils.data import DataLoader, TensorDataset -from q2_ritme.feature_space._process_train import ( +from q2_ritme.feature_space._process_trac_specific import ( _preprocess_taxonomy_aggregation, create_matrix_from_tree, - process_train, ) +from q2_ritme.feature_space._process_train import process_train +from q2_ritme.model_space._model_trac_calc import min_least_squares_solution def _predict_rmse(model: BaseEstimator, X: np.ndarray, y: np.ndarray) -> float: @@ -145,44 +145,6 @@ def train_linreg( _report_results_manually(linreg, X_train, y_train, X_val, y_val) -def solve_unpenalized_least_squares(cmatrices, intercept=False): - # adapted from classo > misc_functions.py > unpenalised - if intercept: - A1, C1, y = cmatrices - A = np.concatenate([np.ones((len(A1), 1)), A1], axis=1) - C = np.concatenate([np.zeros((len(C1), 1)), C1], axis=1) - else: - A, C, y = cmatrices - - k = len(C) - d = len(A[0]) - M1 = np.concatenate([A.T.dot(A), C.T], axis=1) - M2 = np.concatenate([C, np.zeros((k, k))], axis=1) - M = np.concatenate([M1, M2], axis=0) - b = np.concatenate([A.T.dot(y), np.zeros(k)]) - sol = linalg.lstsq(M, b, rcond=None)[0] - beta = sol[:d] - return beta - - -def min_least_squares_solution(matrices, selected, intercept=False): - """Minimum Least Squares solution for selected features.""" - # adapted from classo > misc_functions.py > min_LS - X, C, y = matrices - beta = np.zeros(len(selected)) - - if intercept: - beta[selected] = solve_unpenalized_least_squares( - (X[:, selected[1:]], C[:, selected[1:]], y), intercept=selected[0] - ) - else: - beta[selected] = solve_unpenalized_least_squares( - (X[:, selected], C[:, selected], y), intercept=False - ) - - return beta - - def _predict_rmse_trac(alpha, log_geom_X, y): y_pred = log_geom_X.dot(alpha[1:]) + alpha[0] return mean_squared_error(y, y_pred, squared=False) diff --git a/q2_ritme/tests/test_static_searchspace.py b/q2_ritme/tests/test_static_searchspace.py index c0270e9..46cf4c4 100644 --- a/q2_ritme/tests/test_static_searchspace.py +++ b/q2_ritme/tests/test_static_searchspace.py @@ -1,7 +1,7 @@ import pandas as pd from qiime2.plugin.testing import TestPluginBase -from q2_ritme.model_space import _static_searchspace as ss +from q2_ritme.model_space import static_searchspace as ss class TestFindNonzeroFeatureIdx(TestPluginBase): diff --git a/q2_ritme/tests/test_static_trainables.py b/q2_ritme/tests/test_static_trainables.py index 4ad0478..41ae92d 100644 --- a/q2_ritme/tests/test_static_trainables.py +++ b/q2_ritme/tests/test_static_trainables.py @@ -11,7 +11,7 @@ from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error -from q2_ritme.model_space import _static_trainables as st +from q2_ritme.model_space import static_trainables as st class TestHelperFunctions(TestPluginBase): @@ -73,9 +73,9 @@ def setUp(self): self.seed_data = 0 self.seed_model = 0 - @patch("q2_ritme.model_space._static_trainables.process_train") - @patch("q2_ritme.model_space._static_trainables.ElasticNet") - @patch("q2_ritme.model_space._static_trainables._report_results_manually") + @patch("q2_ritme.model_space.static_trainables.process_train") + @patch("q2_ritme.model_space.static_trainables.ElasticNet") + @patch("q2_ritme.model_space.static_trainables._report_results_manually") def test_train_linreg(self, mock_report, mock_linreg, mock_process_train): # define input parameters config = {"fit_intercept": True, "alpha": 0.1, "l1_ratio": 0.5} @@ -105,9 +105,9 @@ def test_train_linreg(self, mock_report, mock_linreg, mock_process_train): mock_linreg_instance.fit.assert_called_once() mock_report.assert_called_once() - @patch("q2_ritme.model_space._static_trainables.process_train") - @patch("q2_ritme.model_space._static_trainables.RandomForestRegressor") - @patch("q2_ritme.model_space._static_trainables._report_results_manually") + @patch("q2_ritme.model_space.static_trainables.process_train") + @patch("q2_ritme.model_space.static_trainables.RandomForestRegressor") + @patch("q2_ritme.model_space.static_trainables._report_results_manually") def test_train_rf(self, mock_report, mock_rf, mock_process_train): # Arrange config = {"n_estimators": 100, "max_depth": 10} @@ -138,10 +138,10 @@ def test_train_rf(self, mock_report, mock_rf, mock_process_train): # def test_train_nn(self, mock_adam, mock_neural_net, mock_process_train): # # todo: add unit test for pytorch NN - @patch("q2_ritme.model_space._static_trainables.process_train") - @patch("q2_ritme.model_space._static_trainables.xgb.DMatrix") - @patch("q2_ritme.model_space._static_trainables.xgb.train") - @patch("q2_ritme.model_space._static_trainables.xgb_cc") + @patch("q2_ritme.model_space.static_trainables.process_train") + @patch("q2_ritme.model_space.static_trainables.xgb.DMatrix") + @patch("q2_ritme.model_space.static_trainables.xgb.train") + @patch("q2_ritme.model_space.static_trainables.xgb_cc") def test_train_xgb( self, mock_checkpoint, mock_xgb_train, mock_dmatrix, mock_process_train ): @@ -192,11 +192,11 @@ def test_train_xgb( mock_xgb_train.assert_called_once() mock_checkpoint.assert_called_once() - @patch("q2_ritme.model_space._static_trainables.seed_everything") - @patch("q2_ritme.model_space._static_trainables.process_train") - @patch("q2_ritme.model_space._static_trainables.load_data") - @patch("q2_ritme.model_space._static_trainables.NeuralNet") - @patch("q2_ritme.model_space._static_trainables.Trainer") + @patch("q2_ritme.model_space.static_trainables.seed_everything") + @patch("q2_ritme.model_space.static_trainables.process_train") + @patch("q2_ritme.model_space.static_trainables.load_data") + @patch("q2_ritme.model_space.static_trainables.NeuralNet") + @patch("q2_ritme.model_space.static_trainables.Trainer") def test_train_nn( self, mock_trainer, diff --git a/q2_ritme/tune_models.py b/q2_ritme/tune_models.py index 4037a0b..e40eeb8 100644 --- a/q2_ritme/tune_models.py +++ b/q2_ritme/tune_models.py @@ -9,8 +9,8 @@ from ray.air.integrations.mlflow import MLflowLoggerCallback from ray.tune.schedulers import AsyncHyperBandScheduler, HyperBandScheduler -from q2_ritme.model_space import _static_searchspace as ss -from q2_ritme.model_space import _static_trainables as st +from q2_ritme.model_space import static_searchspace as ss +from q2_ritme.model_space import static_trainables as st model_trainables = { # model_type: trainable From 4306535221bd47812a9af69e29cd7433f4babfed Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Wed, 22 May 2024 18:28:51 +0200 Subject: [PATCH 24/28] fix import --- q2_ritme/evaluate_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/q2_ritme/evaluate_models.py b/q2_ritme/evaluate_models.py index 5505d3c..e9219f7 100644 --- a/q2_ritme/evaluate_models.py +++ b/q2_ritme/evaluate_models.py @@ -12,7 +12,9 @@ from ray.air.result import Result from sklearn.metrics import mean_squared_error -from q2_ritme.feature_space._process_train import _preprocess_taxonomy_aggregation +from q2_ritme.feature_space._process_trac_specific import ( + _preprocess_taxonomy_aggregation, +) from q2_ritme.feature_space.transform_features import transform_features from q2_ritme.model_space.static_trainables import NeuralNet From 920dfcd7fc7b154a5b2e32b9202fd451e529be93 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Thu, 23 May 2024 09:47:44 +0200 Subject: [PATCH 25/28] try to fix GHA error --- ci/recipe/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/recipe/meta.yaml b/ci/recipe/meta.yaml index 61c13c5..c87e665 100644 --- a/ci/recipe/meta.yaml +++ b/ci/recipe/meta.yaml @@ -14,7 +14,7 @@ build: requirements: host: - python {{ python }} - - setuptools + - setuptools==69.5.1 - pip run: From 342e7d6d6dd8e8de7833f32e1edcbe265579a495 Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Thu, 23 May 2024 10:01:44 +0200 Subject: [PATCH 26/28] try to fix GHA error again --- ci/recipe/meta.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/recipe/meta.yaml b/ci/recipe/meta.yaml index c87e665..c54c1ff 100644 --- a/ci/recipe/meta.yaml +++ b/ci/recipe/meta.yaml @@ -38,6 +38,8 @@ requirements: - scipy - scikit-learn - scikit-bio + # needs to be pinned due to deprecation of pkg_resources in v70 + - setuptools==69.5.1 - torchvision - zipp # TODO: build package from GH or pypip From 5ddcff2dafc17e28127f52052793ea3f143c4fde Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Thu, 23 May 2024 14:28:37 +0200 Subject: [PATCH 27/28] adding unit tests --- q2_ritme/tests/test_static_searchspace.py | 9 ++++ q2_ritme/tests/test_static_trainables.py | 62 +++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/q2_ritme/tests/test_static_searchspace.py b/q2_ritme/tests/test_static_searchspace.py index 46cf4c4..86e93cb 100644 --- a/q2_ritme/tests/test_static_searchspace.py +++ b/q2_ritme/tests/test_static_searchspace.py @@ -72,6 +72,15 @@ def test_get_linreg_space(self): self.assertIn("alpha", linreg_space) self.assertIn("l1_ratio", linreg_space) + def test_get_trac_space(self): + trac_space = ss.get_trac_space(self.train_val) + + self.assertIsInstance(trac_space, dict) + self.assertEqual(trac_space["model"], "trac") + self.assertEqual(trac_space["data_transform"], None) + self.assertEqual(trac_space["data_alr_denom_idx"], None) + self.assertIn("lambda", trac_space) + def test_get_rf_space(self): rf_space = ss.get_rf_space(self.train_val) diff --git a/q2_ritme/tests/test_static_trainables.py b/q2_ritme/tests/test_static_trainables.py index 41ae92d..02d5e64 100644 --- a/q2_ritme/tests/test_static_trainables.py +++ b/q2_ritme/tests/test_static_trainables.py @@ -6,6 +6,7 @@ import numpy as np import pandas as pd +import skbio import torch from qiime2.plugin.testing import TestPluginBase from sklearn.linear_model import LinearRegression @@ -105,6 +106,67 @@ def test_train_linreg(self, mock_report, mock_linreg, mock_process_train): mock_linreg_instance.fit.assert_called_once() mock_report.assert_called_once() + @patch("q2_ritme.model_space.static_trainables.process_train") + @patch("q2_ritme.model_space.static_trainables.create_matrix_from_tree") + @patch("q2_ritme.model_space.static_trainables._preprocess_taxonomy_aggregation") + @patch("q2_ritme.model_space.static_trainables.Classo") + @patch("q2_ritme.model_space.static_trainables.min_least_squares_solution") + @patch("q2_ritme.model_space.static_trainables._report_results_manually_trac") + def test_train_trac( + self, + mock_report, + mock_min_least_squares, + mock_classo, + mock_preprocess_taxonomy, + mock_create_matrix, + mock_process_train, + ): + # Arrange + config = {"lambda": 0.1} + mock_process_train.return_value = (None, None, None, None, None) + mock_create_matrix.return_value = pd.DataFrame() + mock_preprocess_taxonomy.side_effect = [ + (np.array([[1, 2], [3, 4]]), 2), + (np.array([[5, 6], [7, 8]]), 2), + ] + mock_classo.return_value = np.array([0.1, 0.2]) + mock_min_least_squares.return_value = np.array([0.1, 0.2]) + + # Act + st.train_trac( + config, + self.train_val, + self.target, + self.host_id, + self.seed_data, + self.seed_model, + pd.DataFrame(), + skbio.TreeNode(), + ) + + # Assert + mock_process_train.assert_called_once_with( + config, self.train_val, self.target, self.host_id, self.seed_data + ) + mock_create_matrix.assert_called_once() + assert mock_preprocess_taxonomy.call_count == 2 + + # mock_classo.assert_called_once_with doesn't work because matrix is a + # numpy array + kwargs = mock_classo.call_args.kwargs + + self.assertTrue(np.array_equal(kwargs["matrix"][0], np.array([[1, 2], [3, 4]]))) + self.assertTrue(np.array_equal(kwargs["matrix"][1], np.ones((1, 2)))) + self.assertIsNone(kwargs["matrix"][2]) + self.assertEqual(kwargs["lam"], config["lambda"]) + self.assertEqual(kwargs["typ"], "R1") + self.assertEqual(kwargs["meth"], "Path-Alg") + self.assertEqual(kwargs["w"], 0.5) + self.assertEqual(kwargs["intercept"], True) + + mock_min_least_squares.assert_called_once() + mock_report.assert_called_once() + @patch("q2_ritme.model_space.static_trainables.process_train") @patch("q2_ritme.model_space.static_trainables.RandomForestRegressor") @patch("q2_ritme.model_space.static_trainables._report_results_manually") From 378d4add40ec730abf960afc45bffc062c5e578c Mon Sep 17 00:00:00 2001 From: Anja Adamov <57316423+adamovanja@users.noreply.github.com> Date: Thu, 23 May 2024 15:54:32 +0200 Subject: [PATCH 28/28] remove codecov line comments in PR --- .github/codecov.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/codecov.yml diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 0000000..db24720 --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1 @@ +comment: off