stephen sefick
2017-Feb-21 14:12 UTC
[R] Make sure a data frame has been "fun through" a function
Sorry for not being clear. I have never used S3 methods before. Below is
some R code that sketches out my idea. Is this a sensible solution?
test_data <- data.frame(a=1:10, b=1:10, c=1:10)
functionA <- function(x, impossible_genotype){
##some data processing
y <- x
##return S3 to be able to use impossible genotype later
class(y) <- append(class(y),"genotypes")
attr(y, "impossible_genotype") <- impossible_genotype
return(y)
}
test_data_genotypes <- functionA(test_data,
impossible_genotype="Ref")
functionB <- function(x){
##stop if pre-processed with functionA
if(sum(class(x)=="genotypes")!=1){stop("Need to pre-process
data with
functionA")}
##use this later in functionB to
impossible_genotype <- attributes(x)$impossible_genotype
alleles <- c("Ref", "Alt")
coded_genotype <- alleles[alleles!=impossible_genotype]
return(coded_genotype)
}
##stop if not pre-processed with functionA
functionB(test_data)
##processed with functionA
functionB(test_data_genotypes)
On Tue, Feb 21, 2017 at 6:41 AM, David Winsemius <dwinsemius at
comcast.net>
wrote:
>
> > On Feb 20, 2017, at 4:43 PM, stephen sefick <ssefick at
gmail.com> wrote:
> >
> > Hello All,
> >
> > I am writing a package. I would like to encourage the user to look at
the
> > data to rectify errors with function A before utilizing function B to
> code
> > these data as binary. I thought about solving this problem by adding a
> > "flag" in the attributes that could be used downstream in B,
and have a
> > function that adds this "flag" if the user is convinced that
everything
> is
> > okay. This would allow the user to utilize their data as is, if error
> > checking is not necessary. Maybe I am overthinking this. Thanks again.
> > kindest regards,
>
> Still not clear what is needed but there is an `attr<-` function. You
> might get waht you wnat by having function A add an attribute which is then
> checked by B.
>
> --
> David
> >
> > Stephen
> >
> > On Mon, Feb 20, 2017 at 6:24 PM, Charles C. Berry <ccberry at
ucsd.edu>
> wrote:
> >
> >> On Mon, 20 Feb 2017, stephen sefick wrote:
> >>
> >> Hello,
> >>>
> >>> I would like to add something to a data frame that is 1)
invisible to
> the
> >>> user, 2) has no side effects, and 3) I can test for in a
following
> >>> function. Is this possible? I am exploring classes and
attributes and I
> >>> have thought about using a list (but 1 and 2 not satisfied).
Any help
> >>> would
> >>> be greatly appreciated.
> >>>
> >>>
> >> Depends on exactly what you mean by `invisible' and `side
effects'.
> >>
> >> You can do this (but I am not necessarily recommending this):
> >>
> >> add.stuff <- function(x,...){
> >>>
> >> + class(x)<- c("more.stuff",class(x))
> >> + attr(x,"stuff")<- list(...)
> >> + x}
> >>
> >>>
> >>>
> >> And printing and model functions will be unaffected:
> >>
> >> df <- data.frame(a=1:3,b=letters[1:3])
> >>> df2 <- add.stuff(df,comment="wow", length="3
rows")
> >>> df2
> >>>
> >> a b
> >> 1 1 a
> >> 2 2 b
> >> 3 3 c
> >>
> >>> attr(df2,"stuff")
> >>>
> >> $comment
> >> [1] "wow"
> >>
> >> $length
> >> [1] "3 rows"
> >>
> >> all.equal(lm(a~b,df),lm(a~b,df2)) # only call should differ
> >>>
> >> [1] "Component ?call?: target, current do not match when
deparsed"
> >>
> >>>
> >>>
> >> And if you need some generics to take account of the
"stuff" attribute,
> >> you can write the methods to do that.
> >>
> >> ---
> >>
> >> Another solution is to put your data.framne in a package and then
have
> >> other objects hold the 'stuff' stuff. Once your package is
loaded or
> >> imported, the user will have access to the data in a way that
might be
> said
> >> to be `invisible' in ordinary usage.
> >>
> >> ---
> >>
> >> But seriously, you should say *why* you want to do this. There are
> >> probably excellent solutions that do not involve directly altering
the
> >> data.frame and may not involve putting together a package.
> >>
> >> HTH,
> >>
> >> Chuck
> >
> >
> >
> >
> > --
> > Let's not spend our time and resources thinking about things that
are so
> > little or so large that all they really do for us is puff us up and
make
> us
> > feel like gods. We are mammals, and have not exhausted the annoying
> little
> > problems of being mammals.
> >
> > -K. Mullis
> >
> > "A big computer, a complex algorithm and a long time does not
equal
> > science."
> >
> > -Robert Gentleman
> >
> > [[alternative HTML version deleted]]
> >
> > ______________________________________________
> > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
> > https://stat.ethz.ch/mailman/listinfo/r-help
> > PLEASE do read the posting guide http://www.R-project.org/
> posting-guide.html
> > and provide commented, minimal, self-contained, reproducible code.
>
> David Winsemius
> Alameda, CA, USA
>
>
--
Let's not spend our time and resources thinking about things that are so
little or so large that all they really do for us is puff us up and make us
feel like gods. We are mammals, and have not exhausted the annoying little
problems of being mammals.
-K. Mullis
"A big computer, a complex algorithm and a long time does not equal
science."
-Robert Gentleman
[[alternative HTML version deleted]]
Charles C. Berry
2017-Feb-21 16:30 UTC
[R] Make sure a data frame has been "fun through" a function
On Tue, 21 Feb 2017, stephen sefick wrote:> Sorry for not being clear. I have never used S3 methods before. Below is > some R code that sketches out my idea. Is this a sensible solution? >Sure. See comments (untested) inline. Chuck> test_data <- data.frame(a=1:10, b=1:10, c=1:10) > > functionA <- function(x, impossible_genotype){ > ##some data processing > y <- x > > ##return S3 to be able to use impossible genotype later > class(y) <- append(class(y),"genotypes")class(y) <- c("genotypes",class(y))> > attr(y, "impossible_genotype") <- impossible_genotype > > return(y) > } > > test_data_genotypes <- functionA(test_data, impossible_genotype="Ref") > > functionB <- function(x){ > ##stop if pre-processed with functionA > if(sum(class(x)=="genotypes")!=1){stop("Need to pre-process data with > functionA")}if(!(inherits("genotypes")){ stop("Need to pre-process data with functionA")} or in functionA you could skip the class()<- and just set the "impossible_genotypes" attribute to FALSE when there are none such. Then here test if (is.null(attr(x,"impossible_genotypes"))){ stop("Need to pre-process data with functionA") } else { return(alleles) }> > ##use this later in functionB to > impossible_genotype <- attributes(x)$impossible_genotypeimpossible_genotype <- attr(x,"impossible_genotype")> > alleles <- c("Ref", "Alt") > > coded_genotype <- alleles[alleles!=impossible_genotype]maybe `!is.element(alleles,impossible_genotype)' is safer than `!='> > > > return(coded_genotype) > } > > ##stop if not pre-processed with functionA > functionB(test_data) > > ##processed with functionA > functionB(test_data_genotypes) >
William Dunlap
2017-Feb-21 16:52 UTC
[R] Make sure a data frame has been "fun through" a function
Stray attributes on data.frames may or may not survive some simple operations on the data.frame. E.g.,> d <- data.frame(X=1:5, Y=log(1:5), G=factor(rep(c("a","b"),c(2,3)))) > attr(d, "checked") <- TRUE > wasChecked <- function(x) isTRUE(attr(x, "checked")) > wasChecked(d)[1] TRUE> wasChecked(d[1:4,]) # select some rows[1] TRUE> wasChecked(d[,1:2]) # select some columns[1] FALSE> d[1,1] <- 10 # change a single value > wasChecked(d)[1] TRUE> d$NewColumn <- 11:15 # add a column > wasChecked(d)[1] TRUE I don't know if this would be an issue in your case. If it is, you could subclass "data.frame" and define methods so that the operations of interest preserve or remove the attribute in the way that you desire. Bill Dunlap TIBCO Software wdunlap tibco.com On Tue, Feb 21, 2017 at 8:30 AM, Charles C. Berry <ccberry at ucsd.edu> wrote:> On Tue, 21 Feb 2017, stephen sefick wrote: > >> Sorry for not being clear. I have never used S3 methods before. Below is >> some R code that sketches out my idea. Is this a sensible solution? >> > > Sure. See comments (untested) inline. > > Chuck > >> test_data <- data.frame(a=1:10, b=1:10, c=1:10) >> >> functionA <- function(x, impossible_genotype){ >> ##some data processing >> y <- x >> >> ##return S3 to be able to use impossible genotype later >> class(y) <- append(class(y),"genotypes") > > > class(y) <- c("genotypes",class(y)) > >> >> attr(y, "impossible_genotype") <- impossible_genotype >> >> return(y) >> } >> >> test_data_genotypes <- functionA(test_data, impossible_genotype="Ref") >> >> functionB <- function(x){ >> ##stop if pre-processed with functionA >> if(sum(class(x)=="genotypes")!=1){stop("Need to pre-process data with >> functionA")} > > > if(!(inherits("genotypes")){ > stop("Need to pre-process data with functionA")} > > > or in functionA you could skip the class()<- and just set the > "impossible_genotypes" attribute to FALSE when there are none such. > > Then here test > > if (is.null(attr(x,"impossible_genotypes"))){ > stop("Need to pre-process data with functionA") > } else { > return(alleles) > } > > >> >> ##use this later in functionB to >> impossible_genotype <- attributes(x)$impossible_genotype > > > impossible_genotype <- attr(x,"impossible_genotype") >> >> >> alleles <- c("Ref", "Alt") >> >> coded_genotype <- alleles[alleles!=impossible_genotype] > > > maybe `!is.element(alleles,impossible_genotype)' is safer than `!=' > >> >> >> >> return(coded_genotype) >> } >> >> ##stop if not pre-processed with functionA >> functionB(test_data) >> >> ##processed with functionA >> functionB(test_data_genotypes) >> > > ______________________________________________ > R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code.