-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unexpected behavior of transformList #267
Comments
There is maybe a better solution... @mikejiang ? library(flowCore)
#> Warning: package 'flowCore' was built under R version 4.3.2
transforms <- list()
for (i in 1:8){
transforms[[LETTERS[i]]] <- logicleTransform(w = i)
}
# Print the width of "A" (should be 1)
environment(transforms$A@.Data)$w # output: [1] 8
#> [1] 8
environment(transforms$H@.Data)$w # output: [1] 8
#> [1] 8
# each transform/function has its own environment
sapply(transforms, environment)
#> $A
#> <environment: 0x000001f0342bb308>
#>
#> $B
#> <environment: 0x000001f0341d58f8>
#>
#> $C
#> <environment: 0x000001f0341cac30>
#>
#> $D
#> <environment: 0x000001f0340f9820>
#>
#> $E
#> <environment: 0x000001f033db1740>
#>
#> $F
#> <environment: 0x000001f033c91230>
#>
#> $G
#> <environment: 0x000001f033c62d48>
#>
#> $H
#> <environment: 0x000001f0336e89a0>
#
# so we can assign a value in each environment and check
for (i in 1:8) {
assign("w", i, envir = environment(transforms[[i]]))
}
for (i in 1:8) {
print(get("w", envir = environment(transforms[[i]])))
}
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> [1] 5
#> [1] 6
#> [1] 7
#> [1] 8
environment(transforms$A@.Data)$w
#> [1] 1
environment(transforms$H@.Data)$w
#> [1] 8 Created on 2024-02-05 with reprex v2.1.0 |
Hi guys, I worked on this. Here are two functions that allows getting and setting parameters of the function performing the intensity transformation. This should work for any transformation as parameter names are not specified. Let me know if the code is OK and if you will incorporate it flowCore. #' Get the parameters of the function called by the transformation
#'
#' @param trans a transformation list (such as the value returned by
#' transformList or estimateLogicle functions) or a transform map (such as the
#' value returned by arcsinhTransform or logicleTransform functions).
#' @returns a list of parameters of the function called by each transformation
#' of trans.
#' @export
transform_getParams <- function(trans) {
getParams <- function(transMap) {
func <- transMap@f
env_func <- environment(func)
vars <- ls(env_func)
vals <- sapply(vars, function(v) get(v, envir = env_func))
return(vals)
}
if (inherits(trans, "transformMap"))
return(getParams(trans))
if (inherits(trans, "transformList"))
return(lapply(trans@transforms, getParams))
stop("Not implemented for an object of class ", class(trans))
}
#' Set the parameters of the function called by the transformation
#'
#' @param trans a transformation list (such as the value returned by
#' transformList or estimateLogicle functions) or a transform map (such as the
#' value returned by arcsinhTransform or logicleTransform functions).
#' @param params a list of parameters as returned by transfom_getParams
#' function.
#' @returns a transformation list or a single transformation map according to
#' trans.
#' @export
transform_setParams <- function(trans, params) {
setParams <- function(transMap, params_map) {
func <- transMap@f
env_func <- environment(func)
vars <- ls(env_func)
varp <- names(params_map)
comm <- intersect(vars, varp)
if (length(comm)) {
# make a copy of the environment to avoid overwriting
env_copy <- as.environment(as.list(env_func, all.names=TRUE))
# update the copy
for (v in comm) {
assign(v, unname(params_map[v]), envir = env_copy)
}
# assign the copy to the function
environment(transMap@f) <- env_copy
}
return(transMap)
}
if (inherits(trans, "transformMap")) {
# TODO: check params match trans
return(setParams(trans, params))
}
if (inherits(trans, "transformList")) {
# TODO: check params match trans
trans_maps <- trans@transforms
trans_mrks <- names(trans_maps)
params_mrks <- names(params)
comm_mrks <- intersect(trans_mrks, params_mrks)
if (length(comm_mrks)) {
for (mrk in comm_mrks) {
trans_maps[[mrk]] <- setParams(trans_maps[[mrk]], params[[mrk]])
}
}
trans@transforms <- trans_maps
return(trans)
}
stop("Not implemented for an object of class ", class(trans))
} The following tests checks that the original environments are not changed when modifying parameters. library(flowCore)
data(GvHD)
samp <- GvHD[[1]]
## User defined logicle function
lgcl <- logicleTransform( w = 0.5, t= 10000, m =4.5)
trans <- transformList(c("FL1-H", "FL2-H"), lgcl)
## Automatically estimate the logicle transformation based on the data
lgcl <- estimateLogicle(samp, channels = c("FL1-H", "FL2-H", "FL3-H", "FL2-A", "FL4-H"))
(res = transform_getParams(lgcl))
#> $`FL1-H`
#> $`FL1-H`$a
#> [1] 0
#>
#> $`FL1-H`$k
#> transform object 'FL1-H_logicleTransform'
#>
#> $`FL1-H`$m
#> [1] 4.5
#>
#> $`FL1-H`$t
#> [1] 10000
#>
#> $`FL1-H`$transformationId
#> [1] "FL1-H_logicleTransform"
#>
#> $`FL1-H`$w
#> [1] 0
#>
#>
#> $`FL2-H`
#> $`FL2-H`$a
#> [1] 0
#>
#> $`FL2-H`$k
#> transform object 'FL2-H_logicleTransform'
#>
#> $`FL2-H`$m
#> [1] 4.5
#>
#> $`FL2-H`$t
#> [1] 10000
#>
#> $`FL2-H`$transformationId
#> [1] "FL2-H_logicleTransform"
#>
#> $`FL2-H`$w
#> [1] 0
#>
#>
#> $`FL3-H`
#> $`FL3-H`$a
#> [1] 0
#>
#> $`FL3-H`$k
#> transform object 'FL3-H_logicleTransform'
#>
#> $`FL3-H`$m
#> [1] 4.5
#>
#> $`FL3-H`$t
#> [1] 10000
#>
#> $`FL3-H`$transformationId
#> [1] "FL3-H_logicleTransform"
#>
#> $`FL3-H`$w
#> [1] 0
#>
#>
#> $`FL2-A`
#> $`FL2-A`$a
#> [1] 0
#>
#> $`FL2-A`$k
#> transform object 'FL2-A_logicleTransform'
#>
#> $`FL2-A`$m
#> [1] 4.5
#>
#> $`FL2-A`$t
#> [1] 1023
#>
#> $`FL2-A`$transformationId
#> [1] "FL2-A_logicleTransform"
#>
#> $`FL2-A`$w
#> [1] 0
#>
#>
#> $`FL4-H`
#> $`FL4-H`$a
#> [1] 0
#>
#> $`FL4-H`$k
#> transform object 'FL4-H_logicleTransform'
#>
#> $`FL4-H`$m
#> [1] 4.5
#>
#> $`FL4-H`$t
#> [1] 10000
#>
#> $`FL4-H`$transformationId
#> [1] "FL4-H_logicleTransform"
#>
#> $`FL4-H`$w
#> [1] 0
# set params of a transformation Map (ie one marker)
class(res)
#> [1] "list"
res[[1]]$m
#> [1] 4.5
res[[1]]$m = 6.5 # update the list
transform_getParams(lgcl)[[1]]$m # transform is unchanged
#> [1] 4.5
res2 = transform_setParams(lgcl@transforms[[1]], res[[1]])
transform_getParams(res2)$m
#> [1] 6.5
transform_getParams(lgcl)[[1]]$m
#> [1] 4.5
# set params of a transformation List
res[[1]]$m = 7.5 # update into the list
res3 = transform_setParams(lgcl, res)
transform_getParams(res3)[[1]]$m
#> [1] 7.5
transform_getParams(res2)$m
#> [1] 6.5
transform_getParams(lgcl)[[1]]$m
#> [1] 4.5 Created on 2025-01-25 with reprex v2.1.1 |
This is just a scoping issue - switch to using
|
Hi Dillon. Good point. lapply() is achieving the trick and answers Tim's question. The major point in the lapply() code is that the transformation is created within an anonymous function of z, whose environment is reduced to z only. That's the trick. Probably, I was not clear about the goal of my proposal. I want to retrieve the parameters of any transformList in order to print them and to change them. The test shows that the parameters are changed in the transfromList returned by transform_setParams(), without modifying the values of the given/input transformList. Because parameters are in the environment of the transformation function, changing their values does not create a copy but directly change the values in place, which is not the behaviur that I want. Best. |
I want to create a transformList object by supplying a custom "w" parameter for every channel in a loop. However, when I create a list of transformations, the w-parameter is retro-actively modified for earlier items in the list.
For some reason, this can be prevented by calling summary() on the list if flowCore is loaded. However, the list becomes a closure type if flowCore is not loaded...
Is this the expected behavior?
The text was updated successfully, but these errors were encountered: