{ "cells": [ { "cell_type": "markdown", "id": "96d5e526-425b-4319-8043-828208956ef9", "metadata": {}, "source": [ "# Scatter plots\n", "Using `stackview.scatterplot` we can visualize contents of pandas DataFrames. In such a plot you can select objects and visualize the selection. This might be useful for exploring feature extraction parameter spaces." ] }, { "cell_type": "code", "execution_count": 1, "id": "f4ec4d56-e298-40d8-a5b5-836cbcc2897d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'0.7.11'" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "import numpy as np\n", "import stackview\n", "import pandas as pd\n", "from skimage.measure import regionprops_table\n", "from skimage.io import imread\n", "from skimage.filters import threshold_otsu\n", "from skimage.measure import label\n", "import matplotlib.pyplot as plt\n", "\n", "stackview.__version__" ] }, { "cell_type": "markdown", "id": "dfddfa23-9290-425e-b8e5-2fd4d586db9a", "metadata": {}, "source": [ "To demonstrate this, we need an image, a segmentation and a table of extracted features." ] }, { "cell_type": "code", "execution_count": 2, "id": "e252c694-f7af-4b0d-9bbe-e6f682e8ab5c", "metadata": {}, "outputs": [], "source": [ "image = imread('data/blobs.tif')\n", "\n", "# segment image\n", "thresh = threshold_otsu(image)\n", "binary_image = image > thresh\n", "labeled_image = label(binary_image)" ] }, { "cell_type": "code", "execution_count": 3, "id": "9e84d0b9-3b2f-42a9-b28a-b61f729d2b52", "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", "
centroid-0centroid-1areaferet_diameter_maxminor_axis_lengthmajor_axis_lengthaspect_ratioselection
013.21247119.986143433.036.05551316.81906034.9573992.0784391.0
14.27027062.945946185.021.37755811.80385421.0614171.7842831.0
212.568389108.329787658.032.44996128.27826430.2125521.0684021.0
39.806452154.520737434.026.92582423.06407924.5353981.0637931.0
413.545073246.809224477.031.38471019.83305831.1626121.5712461.0
515.757895197.210526285.021.84033017.77053620.5067301.1539741.0
\n", "
" ], "text/plain": [ " centroid-0 centroid-1 area feret_diameter_max minor_axis_length \\\n", "0 13.212471 19.986143 433.0 36.055513 16.819060 \n", "1 4.270270 62.945946 185.0 21.377558 11.803854 \n", "2 12.568389 108.329787 658.0 32.449961 28.278264 \n", "3 9.806452 154.520737 434.0 26.925824 23.064079 \n", "4 13.545073 246.809224 477.0 31.384710 19.833058 \n", "5 15.757895 197.210526 285.0 21.840330 17.770536 \n", "\n", " major_axis_length aspect_ratio selection \n", "0 34.957399 2.078439 1.0 \n", "1 21.061417 1.784283 1.0 \n", "2 30.212552 1.068402 1.0 \n", "3 24.535398 1.063793 1.0 \n", "4 31.162612 1.571246 1.0 \n", "5 20.506730 1.153974 1.0 " ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "properties = regionprops_table(labeled_image, properties=['centroid', 'area', 'feret_diameter_max', 'minor_axis_length', 'major_axis_length'])\n", "\n", "df = pd.DataFrame(properties)\n", "df[\"aspect_ratio\"] = df['major_axis_length'] / df['minor_axis_length']\n", "\n", "num_objects = df.shape[0]\n", "pre_selection = np.zeros(num_objects)\n", "pre_selection[:int(num_objects/2)] = 1\n", "\n", "pre_selection2 = np.zeros(num_objects)\n", "pre_selection2[::2] = 1\n", "\n", "df[\"selection\"] = pre_selection\n", "\n", "df.head(6)" ] }, { "cell_type": "markdown", "id": "80aa7ed0-3d6e-492c-9d41-37e7ec286e7c", "metadata": {}, "source": [ "## Drawing an interactive scatter plot\n", "Next we draw a scatter plot of area versus Feret's diameter. You can select the axes which are plotted and the plot will change. You can also use a lasso-tool to select data points." ] }, { "cell_type": "code", "execution_count": 4, "id": "8aae0e7c-38d3-45d3-91db-0b286c02c600", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "90f8e79f06174f038601ab84bbba8f64", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(VBox(children=(VBox(children=(HBox(children=(Label(value='Axes '), Dropdown(index=2, layout=Lay…" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stackview.scatterplot(df, 'area', 'feret_diameter_max', \"selection\", figsize=(5,4))" ] }, { "cell_type": "markdown", "id": "70165553-2c82-40d4-8db4-5287786b1db2", "metadata": {}, "source": [ "Next we take the selection from the dataframe and visualize this as an image." ] }, { "cell_type": "code", "execution_count": 5, "id": "137e5269-f5f7-4f38-b562-8eccf752b092", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
shape(254, 256)
dtypeuint32
size254.0 kB
min0
max2
\n", "\n", "
" ], "text/plain": [ "StackViewNDArray([[0, 0, 0, ..., 2, 2, 2],\n", " [0, 0, 0, ..., 2, 2, 2],\n", " [0, 0, 0, ..., 2, 2, 2],\n", " ...,\n", " [0, 0, 0, ..., 0, 0, 0],\n", " [0, 0, 0, ..., 0, 0, 0],\n", " [0, 0, 0, ..., 0, 0, 0]], dtype=uint32)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "selection = df[\"selection\"].tolist()\n", "\n", "selected_image = np.take(np.asarray([-1] + list(selection)) * 1 + 1, labeled_image).astype(np.uint32)\n", "\n", "stackview.insight(selected_image)" ] }, { "cell_type": "markdown", "id": "668151cb-4a89-43a8-bbb0-7fd4fe54414a", "metadata": {}, "source": [ "## Interaction\n", "Using some more involved code we can also draw the image and the scatter plot side-by-side and make them interact. You can select data points in the plot on the right and the visualization on the left will be updated accordingly." ] }, { "cell_type": "code", "execution_count": 6, "id": "7b2bbd63-3255-4ada-94a6-b77207c8efaf", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "98ab4d4f5a1f41329a2d7d039cf57d52", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(VBox(children=(HBox(children=(VBox(children=(ImageWidget(height=406, width=409),…" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stackview.clusterplot(image=image,\n", " labels=labeled_image,\n", " df=df,\n", " column_x=\"area\",\n", " column_y=\"aspect_ratio\", \n", " zoom_factor=1.6,\n", " alpha=0.7)" ] }, { "cell_type": "markdown", "id": "f9b8afd9-9f0a-4f3e-967f-f7680de602a9", "metadata": {}, "source": [ "Every time the user selects different data points, the selection in our dataframe is update" ] }, { "cell_type": "code", "execution_count": 7, "id": "0aa32ebb-7539-48e1-a8c4-be0deb255d05", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 1.0\n", "1 1.0\n", "2 1.0\n", "3 1.0\n", "4 1.0\n", " ... \n", "59 0.0\n", "60 0.0\n", "61 0.0\n", "62 0.0\n", "63 0.0\n", "Name: selection, Length: 64, dtype: float64" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[\"selection\"]" ] }, { "cell_type": "markdown", "id": "8c418529-5596-415a-ac1f-7485da7790e0", "metadata": {}, "source": [ "## Custom widgets\n", "You can also build such a side-by-side view with two plots yourself:" ] }, { "cell_type": "code", "execution_count": 10, "id": "d8f85ddd-160e-44f7-a5e3-eb42e032f093", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "6364820096204b41969d4d12eeabd489", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(HBox(children=(VBox(children=(VBox(children=(HBox(children=(Label(value='Axes '), Dropdown(layo…" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import ipywidgets as widgets\n", "from ipywidgets import HBox\n", "\n", "# Program functions that are executed when the selection is changed.\n", "def update2(e=None):\n", " widget2.update()\n", "\n", "def update1(e=None):\n", " widget1.update()\n", " \n", "widget1 = stackview.scatterplot(df, column_x=\"centroid-0\", column_y=\"centroid-1\", selection_changed_callback=update2, markersize=50)\n", "widget2 = stackview.scatterplot(df, column_x=\"area\", column_y=\"aspect_ratio\", selection_changed_callback=update1)\n", "\n", "# Arrange the widgets side by side using HBox\n", "HBox([widget1, widget2])\n" ] }, { "cell_type": "code", "execution_count": null, "id": "e54890a5-e89c-4861-bf5b-8aa1c5f7f697", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }