natverse / nat

NeuroAnatomy Toolbox: An R package for the (3D) visualisation and analysis of biological image data, especially tracings of single neurons.

Home Page:https://natverse.org/nat/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

subset.neuron() doesn't work inside nmapply()

kjheinz opened this issue · comments

I commonly use subset.neuron() inside nmapply() in order to apply the same operation to all members of a neuronlist, but following a recent update this no longer seems to work (I am on nat version 1.9.1.9000).

Example:

y5 = read.neurons.catmaid("annotation:NAMK_putative_PAM-y5_RIGHT")
y5.axon.nodes = nlapply(y5, function(x) distal_to(x, node.pointno = x$tags$KJH_dend_cut))
y5.SMP = nmapply(subset, y5, y5.axon.nodes, invert=T)

is the code I would have used to subset a group of neurons in the past, but throws the following error:
Error in eval(e, x$d, parent.frame()) : object 'dots' not found

I am able to achieve the result I need with the following workaround:

for (i in 1:length(y5)){
  y5.SMP = c(y5.SMP, neuronlist(subset(y5[[i]], y5.axon.nodes[[i]], invert=T)))
}

Hi @kjheinz, thanks a lot for the report. This is indeed a change of behaviour. It actually happens specifically because I have given nmapply a progress bar which introduces an extra wrapper function. See 6a76320.

subset.neuron and friends are actually rather special in their use of non-standard evaluation to allow you to use columns inside the neuron object $d data.frame as part of the subset specification. It will be difficult (but not impossible) to reenable the behaviour you were used to.

Nevertheless, there are other better ways to achieve what you set out to do. I give some examples below starting with your workaround, which does not quite achieve the same result as your original code (missing names and attached metadata data.frame).

y5.SMP=neuronlist()
for (i in 1:length(y5)){
  y5.SMP = c(y5.SMP, neuronlist(subset(y5[[i]], y5.axon.nodes[[i]], invert=T)))
}

y5.SMP.2 = nlapply(y5, function(x) {
  axon.nodes <- distal_to(x, node.pointno = x$tags$KJH_dend_cut)
  prune_vertices(x, axon.nodes)
})

> all.equal(y5.SMP, y5.SMP.2)
[1] "names for current but not for target"                             
[2] "Attributes: < Length mismatch: comparison on first 1 components >"


y5.SMP.3 = nmapply(prune_vertices, y5, y5.axon.nodes)

> all.equal(y5.SMP.3, y5.SMP.2)
[1] TRUE

y5.SMP.4 = nlapply(y5, function(x) {
  axon.nodes <- distal_to(x, node.pointno = x$tags$KJH_dend_cut)
  subset(x, axon.nodes, invert=TRUE)
})

> all.equal(y5.SMP.4, y5.SMP.2)
[1] TRUE

Basically you can either use prune_vertices inside nmapply or apply a function which both calculates the axonal nodes and uses them subset the neuron (using subset or prune_vertices).

Thanks @jefferis ! Your final suggestion is particularly intuitive to me, and seems very obvious now that you've pointed it out. I think I originally devised my approach when I was still finding my feet with nat and co, and I've copied it ever since. Thanks for alerting me to these better approaches!

Have decided not to try and fix this as workarounds are better style.