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.
%classpath config resolver scijava.public https://maven.scijava.org/content/groups/public
%classpath add mvn net.imagej imagej 2.0.0-rc-71
ij = new net.imagej.ImageJ()
"ImageJ v${ij.getVersion()} is ready to go."
Added new repo: scijava.public
ImageJ v2.0.0-rc-71 is ready to go.
BeakerX has some nice table rendering capabilities. You can create a simple table using a standard map data structure:
foodMap = [
"apple": "fruit",
"orange": "fruit",
"broccoli": "vegetable",
"milk": "dairy",
]
By using a list of maps, you can define column headers, as well as more than two columns:
foodListOfMaps = [
["Food": "apple", "Category": "fruit", "Calories": 95],
["Food": "orange", "Category": "fruit", "Calories": 45],
["Food": "broccoli", "Category": "vegetable", "Calories": 50],
["Food": "milk", "Category": "dairy", "Calories": 103],
]
BeakerX tables do not support complex rendering inside each table cell. For example:
import javax.imageio.ImageIO
// Image sources:
// - https://commons.wikimedia.org/wiki/File:Dark_apple.png
// - https://commons.wikimedia.org/wiki/File:Hassaku_fruit_and_cross_section.jpg
// - https://commons.wikimedia.org/wiki/File:Broccoli_DSC00862.png
// - https://commons.wikimedia.org/wiki/File:Milk_glass.jpg
// - https://commons.wikimedia.org/wiki/File:KS_California_strawberry_yogurt.JPG
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")))
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")))
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")))
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")))
// NB: To see BeakerX explode, run the cell with this line uncommented:
//listOfMaps
null
Table
s¶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.
The org.scijava.table.Tables
utility class provides convenience methods for wrapping List
and Map
data structures as read-only SciJava Table
objects:
import org.scijava.table.Tables
foodTable = Tables.wrap(foodListOfMaps, null)
[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]
ij.notebook().display(...)
methods¶To render Table
objects nicely, they must be passed to the ij.notebook().display(Object)
method:
ij.notebook().display((Object) foodTable)
Food | Category | Calories | Image |
---|---|---|---|
apple | fruit | 95 | |
orange | fruit | 45 | |
broccoli | vegetable | 50 | |
milk | dairy | 103 |
For convenience, there is a ij.notebook().display(List)
method, eliminating the need for the Tables.wrap
call here:
ij.notebook().display(foodListOfMaps)
Food | Category | Calories | Image |
---|---|---|---|
apple | fruit | 95 | |
orange | fruit | 45 | |
broccoli | vegetable | 50 | |
milk | dairy | 103 |
There is also a ij.notebook().display(Map)
method:
ij.notebook().display(foodMap)
apple | fruit |
orange | fruit |
broccoli | vegetable |
milk | dairy |
There are additional signatures of the display
method for specifying row and/or column headers:
ij.notebook().display(foodMap, 'Category')
Category | |
---|---|
apple | fruit |
orange | fruit |
broccoli | vegetable |
milk | dairy |
ij.notebook().display(foodListOfMaps, ['A', 'B', 'C', 'D'])
Food | Category | Calories | Image | |
---|---|---|---|---|
A | apple | fruit | 95 | |
B | orange | fruit | 45 | |
C | broccoli | vegetable | 50 | |
D | milk | dairy | 103 |
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:
ByteColumn
stores each table cell as a primitive byte
(int8).ShortColumn
stores each table cell as a primitive short
(int16).IntColumn
stores each table cell as a primitive int
(int32).LongColumn
stores each table cell as a primitive long
(int64).FloatColumn
stores each table cell as a primitive float
(float32).DoubleColumn
stores each table cell as a primitive double
(float64).BoolColumn
stores each table cell as a primitive boolean
.CharColumn
stores each table cell as a primitive char
.GenericColumn
stores each table cell as an Object
.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.
To illustrate this structure, here is an example which creates a SciJava table from scratch:
import org.scijava.table.DoubleColumn
import org.scijava.table.GenericColumn
import org.scijava.table.DefaultGenericTable
// Create two columns.
nameColumn = new GenericColumn("City")
populationColumn = new DoubleColumn("Population")
// Fill the columns with information about the largest cities in the world.
nameColumn.add("Karachi")
populationColumn.add(23500000d)
nameColumn.add("Bejing")
populationColumn.add(21516000d)
nameColumn.add("Sao Paolo")
populationColumn.add(21292893d)
// But actually, the largest city is Shanghai,
// so let's add it at the beginning of the table.
nameColumn.add(0, "Shanghai")
populationColumn.add(0, 24256800d)
// Create the table.
cities = new DefaultGenericTable()
cities.add(nameColumn)
cities.add(populationColumn)
ij.notebook().display((Object) cities)
City | Population |
---|---|
Shanghai | 2.42568E7 |
Karachi | 2.35E7 |
Bejing | 2.1516E7 |
Sao Paolo | 2.1292893E7 |
Here is another way to create a table, using the set(int col, int row, T value)
method:
import org.scijava.table.DefaultGenericTable
colCount = 7
rowCount = 5
spreadsheet = new DefaultGenericTable(colCount, rowCount)
for (col = 0; col < colCount; col++) {
letter = (char) (col + 65) // 0->A, 1->B, etc.
spreadsheet.setColumnHeader(col, "" + letter)
for (row = 0; row < rowCount; row++) {
data = "" + letter + (row + 1)
spreadsheet.set(col, row, data)
}
}
ij.notebook().display((Object) spreadsheet)
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 |
When created this way, the columns are all GenericColumn
instances:
spreadsheet.stream().map{column -> [
"Header": column.getHeader(),
"Column type": column.getClass().getName(),
"Data type": column.getType()
]}.collect()
Read out the header of the second column:
header = cities.get(1).getHeader()
Population
Get a certain column.
populationColumn = cities.get("Population")
[2.42568E7, 2.35E7, 2.1516E7, 2.1292893E7]
Get a value from the first line in the column.
populationOfLargestTown = populationColumn.get(0)
2.42568E7
Table
API methods¶The Table
interface provides many convenience methods. Here is a complete list:
ij.notebook().methods(cities)