FITS Table extensions

In a given table column, cells may contain more than a single value.

Note

The elements of a column of a FITS table extension are stored in the same memory order as in a Julia array. As a result, the rows of a column in a FITS table extension correspond to the last index in the equivalent Julia array. Method permutedims can be used is this convention does not suit you.

Reading a single table column

To read a single column in a FITS table extension as an array arr, call read as:

arr = read(hdu, col)

with hdu the Header Data Unit containing the table and col the column name or number. The names of the columns are given by hdu.column_names and valid column numbers are in the range hdu.columns (see Table HDU Properties for a list of all properties). If col is a string or a symbol, keyword case can be used to specify whether uppercase/lowercase matters (case is false by default).

By default, all rows are read but it is possible to specify which rows to read:

arr = read(hdu, col, rows)

where rows can be an integer to read a single row, a unit range of integers to read these rows, or a colon : to read all the rows (the default). Properties hdu.first_row and hdu.last_row can be used to retrieve the first and last row numbers of hdu.

The read method automatically guess the number of dimensions and the type of the elements of the Julia array representing the column of a table. To improve type stability, the read method takes an optional first argument to specify the type of the result, from the least specific Array to Array{T} or Array{T,N}. For instance:

arr = read(Vector{Float32}, hdu, col)
arr = read(Vector{Float32}, hdu, col, rows)

are both guaranteed to yield an instance of Vector{Float32} (an exception is thrown if the elements of the column cannot be converted to the given type or if the column cells are not scalar numbers).

In fact, the calls read(hdu,col) and read(hdu,col,rows) are respectively equivalent to read(Array,hdu,col) and read(Array,hdu,col,rows).

The read! method can be called to overwrite the contents of an existing array (it must have contiguous entries) with the cells of a column:

read!(arr, hdu, col)

where keyword first may be used to specify the first row to read (the first one, hdu.first_row, by default), the number of rows to read being deduced from the size of arr. Other keywords are anynull and null to deal with undefined values.

Reading several table columns

In the most simple form, reading several table columns is done by one of:

dict = read(hdu, cols)
dict = read(hdu, cols, rows)

where, as for reading a single column, hdu is the Header Data Unit containing the table and rows may be specified to select the rows to read but cols is a vector or a tuple of column names/numbers, or a colon : to read all columns. Specify keyword case=true if columns are specified by their (symbolic) names and the case of these names matters.

The result of reading several columns is a dictionary whose keys are the names of the columns and whose values are the cells of the columns. The keys of the dictionary are the names of the table columns unconverted if keyword case=true and converted to upper-case letters otherwise. If this is not suitable, use the keyword rename to specify a function to modify the names of the columns. For example, with rename=identity the names of the columns will be left unchanged whatever the keyword case while with rename=lowercase the names of the columns will be converted to lower-case letters.

In order to retrieve the units of the columns, specify keyword units=String and get a dictionary whose values are 2-tuples of the form (data,units) with data the column cells and units the column units as a string. The default behavior, that is to get a dictionary whose values are simply be the cells of the columns, corresponds to units=nothing.

To have a dictionary when cols is a single column name/number, just specify the type of the expected result as the leading argument. For example:

dict = read(Dict, hdu)
dict = read(Dict, hdu, cols)
dict = read(Dict, hdu, cols, rows)

Of course, this also works if the cols argument represents several columns without ambiguities.

The leading type argument may be more specific. For example:

dict = read(Dict{String,Array}, hdu, cols)

is the same as with Dict, while:

dict = read(Dict{String,Vector}, hdu, cols, rows)

can only be successful if all read columns have 0-dimensional cells.

If the leading argument is more specific than Dict, the units=String keyword is not allowed and retrieving the units is done by specifying that the values are tuples of an array and a string. For example:

dict = read(Dict{String,Tuple{Array,String}}, hdu, cols)

Using the leading type argument, it is also possible to retrieve a vector of the table columns (in the same order as specified in cols). For example:

vect = read(Vector, hdu, cols)
vect = read(Vector, hdu, cols, rows)
vect = read(Vector{Array}, hdu, cols, rows)

It is also possible to retrieve the units at the same time:

vect = read(Vector, hdu, cols; units=String)
vect = read(Vector, hdu, cols, rows; units=String)
vect = read(Vector{Tuple{Array,String}}, hdu, cols, rows)

With the read! method, the contents of an existing dictionary may be replaced by columns from a FITS table:

read!(dict, hdu)
read!(dict, hdu, cols)
read!(dict, hdu, cols, rows)

where, as for the read method, keywords case and rename may be supplied. Call the merge! method instead to preserve pre-existing contents:

merge!(dict, hdu)
merge!(dict, hdu, cols)
merge!(dict, hdu, cols, rows)

With the push! method, a vector of columns can be augmented with additional columns:

push!(vect, hdu)
push!(vect, hdu, cols)
push!(vect, hdu, cols, rows)

Creating a table extension

To create a new FITS table extension in the FITS file file, call:

hdu = write(file, FitsTableHDU, cols)

with cols defining the columns of the table. Each column definition is a pair name => format where name is the column name while format specifies the type of the column values and, optionally, their units and the size of the column cells. The following definitions are possible:

name => type
name => (type,)
name => (type, units)
name => (type, dims)
name => (type, units, dims)
name => (type, dims, units)

where type is either a Julia type (Number or String) or a letter (see table below), dims is an integer or a tuple of integers, and units is a string. By default, dims = (1,) and units = "".

TypeLetterRemarks
AbstractString'A'ASCII string
Bool'L'
Int8'S'CFITSIO specific
UInt8'B'
Int16'I'
UInt16'U'CFITSIO specific
Int32'J'
UInt32'V'CFITSIO specific
Int64'K'
UInt64'W'CFITSIO specific
Float32'E'
Float64'D'
Complex{Float32}'C'
Complex{Float64}'M'
FitsBit'X'bits

The returned object, hdu, can be used to add FITS keywords to the header of the table and, then, to write column data. Typically:

hdu = write(file, FitsTableHDU, cols)
push!(hdu, key1 => val1)         # add a first header keyword
push!(hdu, key2 => (val2, com2)) # add a second header keyword with a comment
...                              # add other header keywords
write(hdu, col1 => arr1)         # write a first column
write(hdu, col2 => arr2)         # write a second column
...                              # write other columns

where key1 => val1, key2 => (val2,com2), etc. specify header cards, while col1 => arr1, col2 => arr2, etc. specify columns names and associated data. Such a table may be created in a single call:

write(file,
      [key1 => val1, key2 => (val2, com2), ...],
      [col1 => arr1, col2 => arr2, ...])

which follows the write(dest, header, data) convention in EasyFITS with dest the destination, header the header, and data the data.

The header may be specified in any forms accepted by the EasyFITS methods. Similarly, the columns may be specified in various forms as explained below.

Writing table columns

To write a single column into the FITS table extension hdu:

write(hdu, col => arr, ...; first=hdu.first_row, case=false, null=nothing) -> hdu

where col is the column name/number and arr is an array of column values. Column values are converted as needed and are written starting at the row specified by first. The leading dimensions of arr should be the same as those specified by the corresponding TDIMn keyword (with n the column number) and the remaining last dimension, if any, corresponds to the row index of the table.

If col is a column name, keyword case may be used to indicate whether case of letters matters (default is false).

Keyword null may be used to specify the value of undefined elements in arr.

Any number of columns may be specified as subsequent arguments. The same keywords apply to all columns.

Table HDU Properties

The following table lists all properties of a FITS table HDU.

PropertyDescription
nrowsNumber of rows
rowsIndex range of rows
first_rowIndex of first row
last_rowIndex of last row
ncolsNumber of columns
columnsIndex range of columns
first_columnIndex of first column
last_columnIndex of last column
column_nameColumn name accessor
column_namesColumn names
column_numberColumn number accessor
column_unitsColumn units accessor
data_sizeTable dimensions
data_ndimsNumber of table dimensions
data_axesIndices along table dimensions
extnameExtension name
hdunameHDU name
fileAssociated FITS file
numHDU number
typeHDU type: FITS_BINARY_TABLE_HDU
xtensionExtension: "BINTABLE" or "TABLE"

To retrieve the units, the number, or the name of column col in the FITS table hdu object, use the properties column_units, column_number, or column_name properties as follows:

hdu.column_units(col; case=false) -> units::String
hdu.column_name(col; case=false) -> name::String
hdu.column_number(col; case=false) -> number::Int

Keyword case specifies whether the case of letters does matters when col is a (symbolic) name. The result of hdu.column_units(col) is always a string, possibly empty.