{
"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": [
"
Food | Category | Calories | Image |
---|
apple | fruit | 95 | |
orange | fruit | 45 | |
broccoli | vegetable | 50 | |
milk | dairy | 103 | |
"
]
},
"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": [
"Food | Category | Calories | Image |
---|
apple | fruit | 95 | |
orange | fruit | 45 | |
broccoli | vegetable | 50 | |
milk | dairy | 103 | |
"
]
},
"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": [
"apple | fruit |
orange | fruit |
broccoli | vegetable |
milk | dairy |
"
]
},
"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 |
---|
apple | fruit |
orange | fruit |
broccoli | vegetable |
milk | dairy |
"
]
},
"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": [
" | Food | Category | Calories | Image |
---|
A | apple | fruit | 95 | |
B | orange | fruit | 45 | |
C | broccoli | vegetable | 50 | |
D | milk | dairy | 103 | |
"
]
},
"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": [
"City | Population |
---|
Shanghai | 2.42568E7 |
Karachi | 2.35E7 |
Bejing | 2.1516E7 |
Sao Paolo | 2.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": [
"A | B | C | D | E | F | G |
---|
A1 | B1 | C1 | D1 | E1 | F1 | G1 |
A2 | B2 | C2 | D2 | E2 | F2 | G2 |
A3 | B3 | C3 | D3 | E3 | F3 | G3 |
A4 | B4 | C4 | D4 | E4 | F4 | G4 |
A5 | B5 | C5 | D5 | E5 | F5 | G5 |
"
]
},
"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
}