pept.LineData#

class pept.LineData(lines, sample_size=None, overlap=None, columns=['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2'], **kwargs)[source]#

Bases: IterableSamples

A class for PEPT LoR data iteration, manipulation and visualisation.

Generally, PEPT Lines of Response (LoRs) are lines in 3D space, each defined by two points, regardless of the geometry of the scanner used. This class is used for the encapsulation of LoRs (or any lines!), efficiently yielding samples of lines of an adaptive sample_size and overlap.

It is an abstraction over PET / PEPT scanner geometries and data formats, as once the raw LoRs (be they stored as binary, ASCII, etc.) are transformed into the common LineData format, any tracking, analysis or visualisation algorithm in the pept package can be used interchangeably. Moreover, it provides a stable, user-friendly interface for iterating over LoRs in samples - this is useful for tracking algorithms, as they generally take a few LoRs (a sample), produce a tracer position, then move to the next sample of LoRs, repeating the procedure. Using overlapping samples is also useful for improving the tracking rate of the algorithms.

This is the base class for LoR data; the subroutines for transforming other data formats into LineData can be found in pept.scanners. If you’d like to integrate another scanner geometry or raw data format into this package, you can check out the pept.scanners.parallel_screens module as an example. This usually only involves writing a single function by hand; then all attributes and methods from LineData will be available to your new data format. If you’d like to use LineData as the base for other algorithms, you can check out the pept.tracking.peptml.cutpoints module as an example; the Cutpoints class iterates the samples of LoRs in any LineData in parallel, using concurrent.futures.ThreadPoolExecutor.

See also

pept.PointData

Encapsulate points for ease of iteration and plotting.

pept.read_csv

Fast CSV file reading into numpy arrays.

PlotlyGrapher

Easy, publication-ready plotting of PEPT-oriented data.

pept.tracking.Cutpoints

Compute cutpoints from pept.LineData.

Notes

The class saves lines as a C-contiguous numpy array for efficient access in C / Cython functions. The inner data can be mutated, but do not change the number of rows or columns after instantiating the class.

Examples

Initialise a LineData instance containing 10 lines with a sample_size of 3.

>>> import pept
>>> import numpy as np
>>> lines_raw = np.arange(70).reshape(10, 7)
>>> print(lines_raw)
[[ 0  1  2  3  4  5  6]
 [ 7  8  9 10 11 12 13]
 [14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27]
 [28 29 30 31 32 33 34]
 [35 36 37 38 39 40 41]
 [42 43 44 45 46 47 48]
 [49 50 51 52 53 54 55]
 [56 57 58 59 60 61 62]
 [63 64 65 66 67 68 69]]
>>> line_data = pept.LineData(lines_raw, sample_size = 3)
>>> line_data
pept.LineData (samples: 3)
--------------------------
sample_size = 3
overlap = 0
lines =
  (rows: 10, columns: 7)
  [[ 0.  1. ...  5.  6.]
   [ 7.  8. ... 12. 13.]
   ...
   [56. 57. ... 61. 62.]
   [63. 64. ... 68. 69.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}

Access samples using subscript notation. Notice how the samples are consecutive, as overlap is 0 by default.

>>> line_data[0]
pept.LineData (samples: 1)
--------------------------
sample_size = 3
overlap = 0
lines =
  (rows: 3, columns: 7)
  [[ 0.  1. ...  5.  6.]
   [ 7.  8. ... 12. 13.]
   [14. 15. ... 19. 20.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}
>>> line_data[1]
pept.LineData (samples: 1)
--------------------------
sample_size = 3
overlap = 0
lines =
  (rows: 3, columns: 7)
  [[21. 22. ... 26. 27.]
   [28. 29. ... 33. 34.]
   [35. 36. ... 40. 41.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}

Now set an overlap of 2; notice how the number of samples changes:

>>> len(line_data)     # Number of samples
3
>>> line_data.overlap = 2
>>> len(line_data)
8

Notice how rows are repeated from one sample to the next when accessing them, because overlap is now 2:

>>> line_data[0]
pept.LineData (samples: 1)
--------------------------
sample_size = 3
overlap = 0
lines =
  (rows: 3, columns: 7)
  [[ 0.  1. ...  5.  6.]
   [ 7.  8. ... 12. 13.]
   [14. 15. ... 19. 20.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}
>>> line_data[1]
pept.LineData (samples: 1)
--------------------------
sample_size = 3
overlap = 0
lines =
  (rows: 3, columns: 7)
  [[ 7.  8. ... 12. 13.]
   [14. 15. ... 19. 20.]
   [21. 22. ... 26. 27.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}

Now change sample_size to 5 and notice again how the number of samples changes:

>>> len(line_data)
8
>>> line_data.sample_size = 5
>>> len(line_data)
2
>>> line_data[0]
pept.LineData (samples: 1)
--------------------------
sample_size = 5
overlap = 0
lines =
  (rows: 5, columns: 7)
  [[ 0.  1. ...  5.  6.]
   [ 7.  8. ... 12. 13.]
   ...
   [21. 22. ... 26. 27.]
   [28. 29. ... 33. 34.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}
>>> line_data[1]
pept.LineData (samples: 1)
--------------------------
sample_size = 5
overlap = 0
lines =
  (rows: 5, columns: 7)
  [[21. 22. ... 26. 27.]
   [28. 29. ... 33. 34.]
   ...
   [42. 43. ... 47. 48.]
   [49. 50. ... 54. 55.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}

Notice how the samples do not cover the whole input lines_raw array, as the last lines are omitted - think of the sample_size and overlap. They are still inside the inner lines attribute of line_data though:

>>> line_data.lines
array([[ 0.,  1.,  2.,  3.,  4.,  5.,  6.],
       [ 7.,  8.,  9., 10., 11., 12., 13.],
       [14., 15., 16., 17., 18., 19., 20.],
       [21., 22., 23., 24., 25., 26., 27.],
       [28., 29., 30., 31., 32., 33., 34.],
       [35., 36., 37., 38., 39., 40., 41.],
       [42., 43., 44., 45., 46., 47., 48.],
       [49., 50., 51., 52., 53., 54., 55.],
       [56., 57., 58., 59., 60., 61., 62.],
       [63., 64., 65., 66., 67., 68., 69.]])
Attributes
lines(N, M>=7) numpy.ndarray

An (N, M>=7) numpy array that stores the PEPT LoRs as time and cartesian (3D) coordinates of two points defining a line, followed by any additional data. The data columns are then [time, x1, y1, z1, x2, y2, z2, etc.].

sample_sizeint, list[int], pept.TimeWindow or None

Defining the number of LoRs in a sample; if it is an integer, a constant number of LoRs are returned per sample. If it is a list of integers, sample i will have length sample_size[i]. If it is a pept.TimeWindow instance, each sample will span a fixed time window. If None, custom sample sizes are returned as per the samples_indices attribute.

overlapint, pept.TimeWindow or None

Defining the overlapping LoRs between consecutive samples. If int, constant numbers of LoRs are used. If pept.TimeWindow, the overlap will be a constant time window across the data timestamps (first column). If None, custom sample sizes are defined as per the samples_indices attribute.

samples_indices(S, 2) numpy.ndarray

A 2D NumPy array of integers, where row i defines the i-th sample’s start and end row indices, i.e. sample[i] == data[samples_indices[i, 0]:samples_indices[i, 1]]. The sample_size and overlap are simply friendly interfaces to setting the samples_indices.

columns(M,) list[str]

A list of strings with the same number of columns as lines containing each column’s name.

attrsdict[str, Any]

A dictionary of other attributes saved on this class. Attribute names starting with an underscore are considered “hidden”.

__init__(lines, sample_size=None, overlap=None, columns=['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2'], **kwargs)[source]#

LineData class constructor.

Parameters
lines(N, M>=7) numpy.ndarray

An (N, M>=7) numpy array that stores the PEPT LoRs (or any generic set of lines) as time and cartesian (3D) coordinates of two points defining each line, followed by any additional data. The data columns are then [time, x1, y1, z1, x2, y2, z2, etc.].

sample_sizeint, default 0

An int that defines the number of lines that should be returned when iterating over lines. A sample_size of 0 yields all the data as one single sample.

overlapint, default 0

An int that defines the overlap between two consecutive samples that are returned when iterating over lines. An overlap of 0 means consecutive samples, while an overlap of (sample_size - 1) means incrementing the samples by one. A negative overlap means skipping values between samples. An error is raised if overlap is larger than or equal to sample_size.

columnsList[str], default [“t”, “x1”, “y1”, “z1”, “x2”, “y2”, “z2”]

A list of strings corresponding to the column labels in points.

**kwargsextra keyword arguments

Any extra attributes to set in .attrs.

Raises
ValueError

If lines has fewer than 7 columns.

ValueError

If overlap >= sample_size unless sample_size is 0. Overlap has to be smaller than sample_size. Note that it can also be negative.

Methods

__init__(lines[, sample_size, overlap, columns])

LineData class constructor.

copy([deep, data, extra, hidden])

Construct a similar object, optionally with different data.

extra_attrs()

hidden_attrs()

load(filepath)

Load a saved / pickled PEPTObject object from filepath.

plot([sample_indices, ax, alt_axes, ...])

Plot lines from selected samples using matplotlib.

save(filepath)

Save a PEPTObject instance as a binary pickle object.

to_csv(filepath[, delimiter])

Write lines to a CSV file.

Attributes

attrs

columns

data

lines

overlap

sample_size

samples_indices

property lines#
to_csv(filepath, delimiter=' ')[source]#

Write lines to a CSV file.

Write all LoRs stored in the class to a CSV file.

Parameters
filepathfilename or file handle

If filepath is a path (rather than file handle), it is relative to where python is called.

delimiterstr, default ” “

The delimiter used to separate the values in the CSV file.

plot(sample_indices=Ellipsis, ax=None, alt_axes=False, colorbar_col=0)[source]#

Plot lines from selected samples using matplotlib.

Returns matplotlib figure and axes objects containing all lines included in the samples selected by sample_indices. sample_indices may be a single sample index (e.g. 0), an iterable of indices (e.g. [1,5,6]), or an Ellipsis () for all samples.

Parameters
sample_indicesint or iterable or Ellipsis, default Ellipsis

The index or indices of the samples of lines. An int signifies the sample index, an iterable (list-like) signifies multiple sample indices, while an Ellipsis () signifies all samples. The default is (all lines).

axmpl_toolkits.mplot3D.Axes3D object, optional

The 3D matplotlib-based axis for plotting. If undefined, new Matplotlib figure and axis objects are created.

alt_axesbool, default False

If True, plot using the alternative PEPT-style axes convention: z is horizontal, y points upwards. Because Matplotlib cannot swap axes, this is achieved by swapping the parameters in the plotting call (i.e. plt.plot(x, y, z) -> plt.plot(z, x, y)).

colorbar_colint, default -1

The column in the data samples that will be used to color the lines. The default is -1 (the last column).

Returns
fig, axmatplotlib figure and axes objects

Notes

Plotting all lines is very computationally-expensive for matplotlib. It is recommended to only plot a couple of samples at a time, or use the faster pept.plots.PlotlyGrapher.

Examples

Plot the lines from sample 1 in a LineData instance:

>>> lors = pept.LineData(...)
>>> fig, ax = lors.plot(1)
>>> fig.show()

Plot the lines from samples 0, 1 and 2:

>>> fig, ax = lors.plot([0, 1, 2])
>>> fig.show()
property attrs#
property columns#
copy(deep=True, data=None, extra=True, hidden=True, **attrs)#

Construct a similar object, optionally with different data. If extra, extra attributes are propagated; same for hidden.

property data#
extra_attrs()#
hidden_attrs()#
static load(filepath)#

Load a saved / pickled PEPTObject object from filepath.

Most often the full object state was saved using the .save method.

Parameters
filepathfilename or file handle

If filepath is a path (rather than file handle), it is relative to where python is called.

Returns
pept.PEPTObject subclass instance

The loaded object.

Examples

Save a LineData instance, then load it back:

>>> lines = pept.LineData([[1, 2, 3, 4, 5, 6, 7]])
>>> lines.save("lines.pickle")
>>> lines_reloaded = pept.LineData.load("lines.pickle")
property overlap#
property sample_size#
property samples_indices#
save(filepath)#

Save a PEPTObject instance as a binary pickle object.

Saves the full object state, including inner attributes, in a portable binary format. Load back the object using the load method.

Parameters
filepathfilename or file handle

If filepath is a path (rather than file handle), it is relative to where python is called.

Examples

Save a LineData instance, then load it back:

>>> lines = pept.LineData([[1, 2, 3, 4, 5, 6, 7]])
>>> lines.save("lines.pickle")
>>> lines_reloaded = pept.LineData.load("lines.pickle")