{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Working with tables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook covers how to work with tables in a BeakerX Jupyter notebook, including built-in features as well as SciJava's `org.scijava.table` package." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Added new repo: scijava.public\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "dc9f6872-8f30-40b5-9f69-06c718e532cd", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "ImageJ v2.0.0-rc-71 is ready to go." ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%classpath config resolver scijava.public https://maven.scijava.org/content/groups/public\n", "%classpath add mvn net.imagej imagej 2.0.0-rc-71\n", "ij = new net.imagej.ImageJ()\n", "\"ImageJ v${ij.getVersion()} is ready to go.\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Built-in table rendering" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "BeakerX has some nice table rendering capabilities. You can create a simple table using a standard map data structure:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e7e71a88-92fb-42b6-a549-9f5600513d90", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foodMap = [\n", " \"apple\": \"fruit\",\n", " \"orange\": \"fruit\",\n", " \"broccoli\": \"vegetable\",\n", " \"milk\": \"dairy\",\n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By using a list of maps, you can define column headers, as well as more than two columns:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "5f98444e-81a7-4aac-bf1e-1b07e3e6a00f", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "foodListOfMaps = [\n", " [\"Food\": \"apple\", \"Category\": \"fruit\", \"Calories\": 95],\n", " [\"Food\": \"orange\", \"Category\": \"fruit\", \"Calories\": 45],\n", " [\"Food\": \"broccoli\", \"Category\": \"vegetable\", \"Calories\": 50],\n", " [\"Food\": \"milk\", \"Category\": \"dairy\", \"Calories\": 103],\n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Limitations of built-in BeakerX tables\n", "\n", "BeakerX tables do not support complex rendering inside each table cell. For example:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "null" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import javax.imageio.ImageIO\n", "\n", "// Image sources:\n", "// - https://commons.wikimedia.org/wiki/File:Dark_apple.png\n", "// - https://commons.wikimedia.org/wiki/File:Hassaku_fruit_and_cross_section.jpg\n", "// - https://commons.wikimedia.org/wiki/File:Broccoli_DSC00862.png\n", "// - https://commons.wikimedia.org/wiki/File:Milk_glass.jpg\n", "// - https://commons.wikimedia.org/wiki/File:KS_California_strawberry_yogurt.JPG\n", "foodListOfMaps.get(0).put(\"Image\", ImageIO.read(new URL(\"https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Dark_apple.png/120px-Dark_apple.png\")))\n", "foodListOfMaps.get(1).put(\"Image\", ImageIO.read(new URL(\"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Hassaku_fruit_and_cross_section.jpg/120px-Hassaku_fruit_and_cross_section.jpg\")))\n", "foodListOfMaps.get(2).put(\"Image\", ImageIO.read(new URL(\"https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Broccoli_DSC00862.png/120px-Broccoli_DSC00862.png\")))\n", "foodListOfMaps.get(3).put(\"Image\", ImageIO.read(new URL(\"https://upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Milk_glass.jpg/106px-Milk_glass.jpg\")))\n", "\n", "// NB: To see BeakerX explode, run the cell with this line uncommented:\n", "//listOfMaps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## SciJava `Table`s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "SciJava offers a set of interfaces for tables in the `org.scijava.table` package. The base interface is `Table`, which is a `List` of typed `Column`s. These columns offer improved type safety and storage efficiency over the \"maps\" and \"list of maps\" approaches shown above. They can also used to render more complex data using the `ij.notebook().display(...)` methods." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Wrapping lists and maps as SciJava tables\n", "\n", "The `org.scijava.table.Tables` utility class provides convenience methods for wrapping `List` and `Map` data structures as read-only SciJava `Table` objects:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[org.scijava.table.Tables$3$1@21807e, org.scijava.table.Tables$3$1@6dd211e, org.scijava.table.Tables$3$1@f9c82f26, org.scijava.table.Tables$3$1@437b93b]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import org.scijava.table.Tables\n", "foodTable = Tables.wrap(foodListOfMaps, null)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Rendering table data using the `ij.notebook().display(...)` methods" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To render `Table` objects nicely, they must be passed to the `ij.notebook().display(Object)` method:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
FoodCategoryCaloriesImage
applefruit95
orangefruit45
broccolivegetable50
milkdairy103
" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ij.notebook().display((Object) foodTable)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For convenience, there is a `ij.notebook().display(List)` method, eliminating the need for the `Tables.wrap` call here:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
FoodCategoryCaloriesImage
applefruit95
orangefruit45
broccolivegetable50
milkdairy103
" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ij.notebook().display(foodListOfMaps)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is also a `ij.notebook().display(Map)` method:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
applefruit
orangefruit
broccolivegetable
milkdairy
" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ij.notebook().display(foodMap)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are additional signatures of the `display` method for specifying row and/or column headers:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
 Category
applefruit
orangefruit
broccolivegetable
milkdairy
" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ij.notebook().display(foodMap, 'Category')" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
 FoodCategoryCaloriesImage
Aapplefruit95
Borangefruit45
Cbroccolivegetable50
Dmilkdairy103
" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ij.notebook().display(foodListOfMaps, ['A', 'B', 'C', 'D'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Creating a SciJava table from scratch\n", "\n", "There is an API for creating SciJava `Table` objects without wrapping lists or maps. Using this API gives you control over the data type of the columns. The following column types are available:\n", "\n", "* `ByteColumn` stores each table cell as a primitive `byte` (int8).\n", "* `ShortColumn` stores each table cell as a primitive `short` (int16).\n", "* `IntColumn` stores each table cell as a primitive `int` (int32).\n", "* `LongColumn` stores each table cell as a primitive `long` (int64).\n", "* `FloatColumn` stores each table cell as a primitive `float` (float32).\n", "* `DoubleColumn` stores each table cell as a primitive `double` (float64).\n", "* `BoolColumn` stores each table cell as a primitive `boolean`.\n", "* `CharColumn` stores each table cell as a primitive `char`.\n", "* `GenericColumn` stores each table cell as an `Object`.\n", "\n", "`GenericColumn` is the most flexible, but it uses an `Object` for every cell, which can be inefficient. For better space performance, it is encouraged to instead use column types with efficient storage as appropriate. For example, if you know a column will consist only of `short` values, then use a `ShortColumn`. The primitive numeric column types are built on SciJava's `PrimitiveArray` utility classes; e.g., `DoubleColumn` is a column backed by a `DoubleArray`, which is in turn backed by a `double[]` which grows dynamically as needed.\n", "\n", "To illustrate this structure, here is an example which creates a SciJava table from scratch:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
CityPopulation
Shanghai2.42568E7
Karachi2.35E7
Bejing2.1516E7
Sao Paolo2.1292893E7
" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import org.scijava.table.DoubleColumn\n", "import org.scijava.table.GenericColumn\n", "import org.scijava.table.DefaultGenericTable\n", "\n", "// Create two columns.\n", "nameColumn = new GenericColumn(\"City\")\n", "populationColumn = new DoubleColumn(\"Population\")\n", "\n", "// Fill the columns with information about the largest cities in the world.\n", "nameColumn.add(\"Karachi\")\n", "populationColumn.add(23500000d)\n", "nameColumn.add(\"Bejing\")\n", "populationColumn.add(21516000d)\n", "nameColumn.add(\"Sao Paolo\")\n", "populationColumn.add(21292893d)\n", "\n", "// But actually, the largest city is Shanghai,\n", "// so let's add it at the beginning of the table.\n", "nameColumn.add(0, \"Shanghai\")\n", "populationColumn.add(0, 24256800d)\n", "\n", "// Create the table.\n", "cities = new DefaultGenericTable()\n", "cities.add(nameColumn)\n", "cities.add(populationColumn)\n", "\n", "ij.notebook().display((Object) cities)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is another way to create a table, using the `set(int col, int row, T value)` method:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
ABCDEFG
A1B1C1D1E1F1G1
A2B2C2D2E2F2G2
A3B3C3D3E3F3G3
A4B4C4D4E4F4G4
A5B5C5D5E5F5G5
" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import org.scijava.table.DefaultGenericTable\n", "colCount = 7\n", "rowCount = 5\n", "spreadsheet = new DefaultGenericTable(colCount, rowCount)\n", "for (col = 0; col < colCount; col++) {\n", " letter = (char) (col + 65) // 0->A, 1->B, etc.\n", " spreadsheet.setColumnHeader(col, \"\" + letter)\n", " for (row = 0; row < rowCount; row++) {\n", " data = \"\" + letter + (row + 1)\n", " spreadsheet.set(col, row, data)\n", " }\n", "}\n", "ij.notebook().display((Object) spreadsheet)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When created this way, the columns are all `GenericColumn` instances:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "80d75a8f-9976-4c74-9d3c-e5a6630e2665", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "spreadsheet.stream().map{column -> [\n", " \"Header\": column.getHeader(),\n", " \"Column type\": column.getClass().getName(),\n", " \"Data type\": column.getType()\n", "]}.collect()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reading information from tables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Read out the header of the second column:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Population" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "header = cities.get(1).getHeader()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get a certain column." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2.42568E7, 2.35E7, 2.1516E7, 2.1292893E7]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "populationColumn = cities.get(\"Population\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get a value from the first line in the column." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.42568E7" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "populationOfLargestTown = populationColumn.get(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### List of `Table` API methods" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `Table` interface provides many convenience methods. Here is a complete list:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b84acd75-58b5-4bee-95a3-8fc43ab98786", "version_major": 2, "version_minor": 0 }, "method": "display_data" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ij.notebook().methods(cities)" ] } ], "metadata": { "kernelspec": { "display_name": "Groovy", "language": "groovy", "name": "groovy" }, "language_info": { "codemirror_mode": "groovy", "file_extension": ".groovy", "mimetype": "", "name": "Groovy", "nbconverter_exporter": "", "version": "2.4.3" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "307px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 2 }