knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(ARCOS)
library(data.table)
library(ggplot2)
library(ggthemes)

library(testthat)

## Custom functions

# Wrapper function for testing
myTest = function(inDTcalc, inFtrue, inDir = "testdata") {

  resTest = testthat::test_that("test chunk", {
    locDTtrueRes = fread(file.path(system.file(inDir, 
                                               package="ARCOS"),
                                   inFtrue))
    attr(inDTcalc, "sorted") = NULL
    attr(inDTcalc, "class") = c("data.table", "data.frame")

    if ("clTrackID" %in% names(locDTtrueRes)) setnames(inDTcalc, "collid", "clTrackID")
    expect_equal(inDTcalc, 
                 locDTtrueRes)
  })
}

Introduction

Demonstration of the ARCOS algorithm on synthetic data used for unit testing.

Link 1 frame

Only spatial clusters in consecutive frames are linked.

Test 1 - 1 central

A single object that is active at frames 2, [4,5], and [7,9]. There are gaps of inactivity at frames 3 and 6.

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", package="ARCOS"), "1central_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")

ARCOS::plotTracks(dts, size = 3)

The parameter nPrev = 1L tells the tracking algorithm to link events only from consecutive frames, i.e. it looks back only 1 frame. Hence, clustering will yield 3 events at frames 2, [4,5], [7,8,9].

dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "1central_res.csv")

Test 2 - 3 spreading

Consider 7 objects that become subsequently active every 2 frames. It mimics an activity wave spreading from object #4 to neighbouring objects.

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "3spreading_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)

By linking events that are 1 frame apart (parameter nPrev = 1L), the tracking algorithm identifies 3 collective events.

dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "3spreading_res.csv")

Test 3 - 5 overlapping

Consider two objects 2 spatial units apart that beocme active at overlapping time points.

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "5overlapping_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)
dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "5overlapping_res.csv")

Test 4 - 6 overlapping

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "6overlapping_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)
dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "6overlapping_res.csv")

Test 5 - 1 central growing

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "1centralGrowing_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)
dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "1centralGrowing_res.csv")

Test 6 - 2 central growing

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2centralGrowing_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)
dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "2centralGrowing_res.csv")

Test 7 - 2 with 1 common symmetric

This test addresses a problem of two different activation events (objects #1 and 2) that spread to a common, equidistant neighbour (object #3).

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2with1commonSym_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)

Since there's no obvious way to treat this symmetric case, the algorithm assumes that the common neighbour becomes active due to one of the initiators. Here, the activation wave #2 from object #2 spreads to object #3, while the activation wave #1 from object #1 is confined only to that object.

dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "2with1commonSym_res.csv")

Test 8 - 2 with 1 common asymmetric

A similar situation to the previous case but object #3 is no longer equidistant to objects #1 and 2. Instead, it is closer to object #1.

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2with1commonAsym_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)

Since the neighbourhood cut-off is 1 distance unit (parameter eps), the collective activation #1 from object #1 spreads to object #3.

dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "2with1commonAsym_res.csv")

Multiple new objects

Test 1 - simultaneous

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "multipleSimulNewObjects_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "multipleSimulNewObjects_out.csv")

Test 2 - 4 new objects at once after 1

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "multipleSubseq4NewObjectsAfter1_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "multipleSubseq4NewObjectsAfter1_out.csv")

Test 3 - 3 new objects at once after 2

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "multipleSubseq3NewObjectsAfter2_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "multipleSubseq3NewObjectsAfter2_out.csv")

Test 4 - 4 new objects at once after 2

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "multipleSubseq4NewObjectsAfter2_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "multipleSubseq4NewObjectsAfter2_out.csv")

Test 5 - 4 subsequently

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "multipleSubseqNewObjects_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)
ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "multipleSubseqNewObjects_out.csv")

Split, cross, merge

Test 1 - split from single

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "1objSplit_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "1objSplit_res.csv")

Test 2 - split from 2 objects

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2objSplit_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "2objSplit_res.csv")

Test 3 - cross 2 objects

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2objCross_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "2objCross_res.csv")

Test 4 - cross 2 objects with common

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2objCrossCommon_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "2objCrossCommon_res.csv")

Test 5 - merge & split 2 objects with common

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2objMergeSplitCommon_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "2objMergeSplitCommon_res.csv")

Test 6 - merge & split 2 objects crossing

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2objMergeSplitCross_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "2objMergeSplitCross_res.csv")

Test 7 - merge & split 2 objects near

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2objMergeSplitNear_in.csv"),
                              colPos = "pos",
                              colFrame = "t",
                              colIDobj = "id")

ARCOS::plotTracks(dts, size = 3)
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(t, id, collid)],
       "2objMergeSplitNear_res.csv")

Test 8 - 2 clusters, 1 delayed

This test illustrates an important feature of the algorithm. At time 5, object #2 is within the eps radius 1.5 of object #1. This triggers the inheritance of cluster IDs of the nearest neighbours from time 4. However, the nearest neighbour of object #3 is object #4 (1.9 distance vs. 2 to object #1). Consequently, collective IDs of the new cluster are "split" between ID #1 and #2.

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2clusters1delayed_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")

ARCOS::plotTracks(dts, size = 3) +
  scale_x_continuous(breaks = 1:7) +
  theme(panel.grid.minor = element_blank())
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.5, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3) +
  scale_x_continuous(breaks = 1:7) +
  theme(panel.grid.minor = element_blank())
myTest(dcoll[,
             .(time, trackID, collid)],
       "2clusters1delayed_out.csv")

Test 9 - 2 clusters merging

This test illustrates another important feature of the algorithm.

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2clustersMerging_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")

ARCOS::plotTracks(dts, size = 3) +
  scale_x_continuous(breaks = 1:10) +
  theme(panel.grid.minor = element_blank())
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.5, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3) +
  scale_x_continuous(breaks = 1:10) +
  theme(panel.grid.minor = element_blank())
myTest(dcoll[,
             .(time, trackID, collid)],
       "2clustersMerging_out.csv")

Test 10 - 2 big clusters crossing

This test illustrates another important feature of the algorithm.

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "2clustersCrossing_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")

ARCOS::plotTracks(dts, size = 3) +
  scale_x_continuous(breaks = 1:10) +
  theme(panel.grid.minor = element_blank())
dcoll = ARCOS::trackColl(obj = dts, 
                         eps = 1.5, 
                         minClSz = 1L, 
                         nPrev = 1L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3) +
  scale_x_continuous(breaks = 1:10) +
  theme(panel.grid.minor = element_blank())
myTest(dcoll[,
             .(time, trackID, collid)],
       "2clustersCrossing_out.csv")

Link 2 frames

Spatial clusters are linked 2 frames back, thus a one-frame-gap is allowed in a collective event.

Test 1 - 1 central

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", package="ARCOS"), "1central_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")

ARCOS::plotTracks(dts, size = 3)

Here parameter nPrev = 2L, which links events from two consecutive frames, i.e. it looks back 2 frames. Hence, clustering will yield 1 continuous events.

dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 2L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "1central2prev_res.csv")

Test 2 - 3 spreading

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "3spreading_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)
dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 2L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "3spreading2prev_res.csv")

Test 3 - 5 overlapping

dts = ARCOS::loadDataFromFile(file.path(system.file("testdata", 
                                                    package="ARCOS"), 
                                        "5overlapping_in.csv"),
                              colPos = "x",
                              colMeas = "m",
                              colFrame = "time",
                              colIDobj = "trackID")


ARCOS::plotTracks(dts[m>0], size = 3)
dcoll = ARCOS::trackColl(obj = dts[m>0], 
                         eps = 1.0, 
                         minClSz = 1L, 
                         nPrev = 2L,
                         deb = F)

ARCOS::plotTracks(dcoll, size = 3)
myTest(dcoll[,
             .(time, trackID, collid)],
       "5overlapping2prev_res.csv")


dmattek/ARCOS documentation built on Dec. 5, 2024, 11:02 p.m.