CasaCore.Tables

CasaCore.Tables

Load this module by running

using CasaCore.Tables

The Tables module is used to interact with CasaCore tables. This is a common data format in radio astronomy. For example CASA measurement sets and CASA calibration tables are simply CasaCore tables with a standard set of columns, keywords, and subtables.

Tables

mutable struct Table

This type is used to interact with CasaCore tables (including measurement sets).

Fields:

  • path - the path to the table

  • status - the current status of the table

  • ptr - the pointer to the table object

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
Table: /tmp/my-table.ms (read/write)

julia> Tables.add_rows!(table, 3)
3

julia> table["DATA"] = Complex64[1+2im, 3+4im, 5+6im]
3-element Array{Complex{Float32},1}:
 1.0+2.0im
 3.0+4.0im
 5.0+6.0im

julia> Tables.close(table)
closed::CasaCore.Tables.TableStatus = 0

julia> table = Tables.open("/tmp/my-table.ms")
Table: /tmp/my-table.ms (read-only)

julia> table["DATA"]
3-element Array{Complex{Float32},1}:
 1.0+2.0im
 3.0+4.0im
 5.0+6.0im

julia> Tables.delete(table)

See also: Tables.create, Tables.open, Tables.close, Tables.delete

source
create(path)

Create a CasaCore table at the given path.

Arguments:

  • path - the path where the table will be created

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
Table: /tmp/my-table.ms (read/write)

julia> Tables.delete(table)

See also: Tables.open, Tables.close, Tables.delete

source
CasaCore.Tables.openFunction.
open(path; write=false)

Open the CasaCore table at the given path.

Arguments:

  • path - the path to the table that will be opened

Keyword Arguments:

  • write - if false (the default) the table will be opened read-only

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
Table: /tmp/my-table.ms (read/write)

julia> table′ = Tables.open("/tmp/my-table.ms")
Table: /tmp/my-table.ms (read-only)

julia> table″ = Tables.open("/tmp/my-table.ms", write=true)
Table: /tmp/my-table.ms (read/write)

julia> Tables.close(table′)
       Tables.close(table″)
       Tables.delete(table)

See also: Tables.create, Tables.close, Tables.delete

source
CasaCore.Tables.closeFunction.
close(table)

Close the given CasaCore table.

Arguments:

  • table - the table to be closed

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
Table: /tmp/my-table.ms (read/write)

julia> Tables.close(table)
closed::CasaCore.Tables.TableStatus = 0

julia> Tables.delete(table)

See also: Tables.create, Tables.open, Tables.delete

source
delete(table)

Close and delete the given CasaCore table.

Arguments:

  • table - the table to be deleted

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
Table: /tmp/my-table.ms (read/write)

julia> Tables.delete(table)

See also: Tables.create, Tables.open, Tables.create

source
Tables.num_rows(table)

Returns the number of rows in the given table.

Arguments:

  • table - the relevant table

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
       Tables.num_rows(table)
0

julia> Tables.add_rows!(table, 10)
       Tables.num_rows(table)
10

julia> Tables.remove_rows!(table, 1:2:10)
       Tables.num_rows(table)
5

julia> Tables.delete(table)

See also: Tables.num_columns, Tables.num_keywords

source
Tables.add_rows!(table, number)

Add the given number of rows to the table.

Arguments:

  • table - the relevant table

  • number - the number of rows that will be added to the table

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
       Tables.add_rows!(table, 10)
       Tables.num_rows(table)
10

julia> Tables.add_rows!(table, 123)
       Tables.num_rows(table)
133

julia> Tables.delete(table)

See also: Tables.remove_rows!

source
Tables.remove_rows!(table, rows)

Remove the specified rows from the table.

Arguments:

  • tables - the relevant table

  • rows - the row or rows that will be deleted from the table

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
       Tables.add_rows!(table, 10)
       Tables.remove_rows!(table, 1:2:10)
       Tables.num_rows(table)
5

julia> Tables.remove_rows!(table, 4)
       Tables.num_rows(table)
4

julia> Tables.remove_rows!(table, [1, 2, 3])
       Tables.num_rows(table)
1

julia> Tables.delete(table)

See also: Tables.add_rows!

source

Columns

Columns are accessed by name. Some common table names (used in CASA measurement sets) are UVW (the baseline coordinates), DATA (the uncalibrated data), and CORRECTED_DATA (the calibrated data).

For example to read and write the entire DATA column from a measurement set:

julia> table = Tables.create("/tmp/my-table.ms")
       Tables.add_rows!(table, 100)
       Npol  =   4 # number of polarizations
       Nfreq =  50 # number of frequency channels
       Nbase = 100 # number of baselines
       data = rand(Complex64, Npol, Nfreq, Nbase)
       table["DATA"] = data # creates the DATA column if it doesn't already exist
       data == table["DATA"]
true

julia> Tables.delete(table)
Warning

CasaCore.jl will throw a CasaCoreTablesError exception if you try to overwrite a column with an array of the incorrect size or element type. A column that contains floats cannot be overwritten with an array of ints.

Tables.num_columns(table)

Returns the number of columns in the given table.

Arguments:

  • table - the relevant table

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
       Tables.num_columns(table)
0

julia> Tables.add_rows!(table, 10)
       table["TEST_COLUMN"] = randn(10)
       Tables.num_columns(table)
1

julia> Tables.delete(table)

See also: Tables.num_rows, Tables.num_keywords

source
Tables.remove_column!(table, column)

Remove the specified column from the table.

Arguments:

  • table - the relevant table

  • column - the column that will be removed from the table

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
       Tables.add_rows!(table, 10)
       table["TEST"] = rand(Bool, 10)
       Tables.num_columns(table)
1

julia> Tables.remove_column!(table, "TEST")
       Tables.num_columns(table)
0

julia> Tables.delete(table)

See also: Tables.num_columns

source

Cells

If you do not want to read or write to an entire column, you can instead pick a single row of the column (ie. a cell). For example, the length of the 123rd baseline in a measurement set can be computed by:

julia> table = Tables.create("/tmp/my-table.ms")
       Nbase = 500 # number of baselines
       Tables.add_rows!(table, 500)
       uvw = 100 .* randn(3, Nbase) # create a random set of baselines
       table["UVW"] = uvw # creates the UVW column if it doesn't already exist
       uvw[:, 123] == table["UVW", 123]
true

julia> table["UVW", 123] = [100., 50, 0.]
       table["UVW", 123]
3-element Array{Float64,1}:
 100.0
  50.0
   0.0

julia> Tables.delete(table)

The number of rows in the table can be obtained with Tables.num_rows. Note also that the indexing order is column first, row second. This is opposite from the usual matrix convention where the first index specifies the row.

Important

Julia is 1-indexed programming language. This means that the first element of an array x is accessed with x[1] instead of x[0] (as is the case for C and Python). Similarly, the first row of a table is row number 1. Attempting to access row number 0 will throw a CasaCoreTablesError because this row does not exist.

Keywords

Keywords are accessed using the kw"..." string macro. For example:

julia> table = Tables.create("/tmp/my-table.ms")
       table[kw"MS_VERSION"] = 2.0 # set the value of the "MS_VERSION" keyword
       table[kw"MS_VERSION"]       # read the value of the "MS_VERSION" keyword
2.0

julia> Tables.delete(table)
num_keywords(table)

Returns the number of keywords associated with the given table.

Arguments:

  • table - the relevant table

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
       Tables.num_keywords(table)
0

julia> table[kw"RICK_PERLEY_IS_A_BOSS"] = true
       Tables.num_keywords(table)
1

julia> table[kw"NOT_SO_BAD"] = "yourself"
       Tables.num_keywords(table)
2

julia> Tables.delete(table)

See also: Tables.num_rows, Tables.num_columns

source
Tables.remove_keyword!(table, keyword)

Remove the specified keyword from the table.

Arguments:

  • table - the relevant table

  • keyword - the keyword to be removed

Usage:

julia> table = Tables.create("/tmp/my-table.ms")
       table[kw"HELLO"] = "world"
       Tables.num_keywords(table)
1

julia> Tables.remove_keyword!(table, kw"HELLO")
       Tables.num_keywords(table)
0

julia> Tables.delete(table)

See also: Tables.num_keywords

source

Subtables

Subtables will be automatically opened by reading the appropriate keyword. These tables need to be closed when you are done using them (just as for a regular table).

julia> table = Tables.create("/tmp/my-table.ms")
       subtable = Tables.create("/tmp/my-sub-table.ms")
       subtable[kw"SECRET_CODE"] = Int32(42)
       table[kw"SUBTABLE"] = subtable
       Tables.close(subtable)
closed::CasaCore.Tables.TableStatus = 0

julia> subtable = table[kw"SUBTABLE"] # re-open the subtable
       subtable[kw"SECRET_CODE"]
42

julia> Tables.delete(table)
       Tables.delete(subtable)

Best Practices

Type Stability

Julia is a dynamically typed language. Because of this we can write statements like column = table["column"] without knowing the type of the column ahead of time. If the column contains floats (Float32), Julia will do the right thing. If the column contains doubles (Float64), Julia will do the right thing. As a user, we did not need to know whether this column contains floats or doubles ahead of time.

However Julia also performs "type-inference". This means that Julia will attempt to deduce the types of your variables. If the types of your variables can be inferred at compile time, Julia will generate more efficient machine code specialized on the types that it inferred. If the types of your variables cannot be inferred at compile time, Julia will need to generate less efficient generic code to account for the uncertainty in the types of your variables.

This concept is important for CasaCore.Tables because the result of table["column"] can be a wide variety of different types, and the actual type isn't known until run time. Now consider the following example:

function add_one_to_data_column(table)
    column = table["DATA"] # type of `column` cannot be inferred
    for idx in eachindex(column)
        column[idx] += 1
    end
    table["DATA"] = column
end

This function will read the DATA column from the given table, add one to each element, and then write the result back to the table. However because the type of column cannot be inferred, the performance of the for-loop will be sub-optimal. We can remedy this problem by moving the computational kernel into a separate function:

function add_one_to_data_column(table)
    column = table["DATA"]
    do_the_for_loop(column) # `do_the_for_loop` specializes on the actual type of `column`
    table["DATA"] = column
end

function do_the_for_loop(column)
    for idx in eachindex(column)
        column[idx] += 1
    end
end

When do_the_for_loop is called, Julia will specialize the function on the actual type of column. That is, the for-loop will be compiled with the knowledge of the actual type of column. This specialization ultimately means that the latter example will generally be faster.

For more information please refer to the performance tips section of the Julia manual.