Bert, you got me. Factors seem to be implemented as a sort of one-way street. 
Mea maxima culpa!

I did some experiments and clearly I misunderstood the way factors in R are set 
up. I made a series of factors of various kinds such as integer and logical and 
character and they all are simply a class of "factor" as they have an attribute 
of 'class' and a typeof() "integer"  as the payload is now a series of small 
integers.  A dictionary of sorts is kept in the attribute of 'levels' but that 
seems to always be of type character as in:

levels(somelogsfac)
[1] "FALSE" "TRUE" 
> typeof(levels(somelogsfac)[1])
[1] "character"


So it sounds like if I read in or create something like a data.frame and I 
convert some columns to factors to get the results I want like more compressed 
storage or maybe get ggplot to use my data in some specified order, I also lose 
any idea of what it was once. That is fine for some purposes such as where the 
info is wanted in text form, perhaps less so if it has to constantly be 
converted back into some other form.

But it seems to be a destructive operation in the sense that once done, there 
is no info preserved as to what was there before. Mind you, that is not really 
a problem as doing many transformations like as.integer() also replaces what 
was with what now is.

But in a sense it can be made reversible if you choose to extend it a bit. 
Below is some code I threw together that if called instead of "factor()" will 
hide away the original type so it can be used later as an argument to as() to 
bring back the original type. This is not a change to the original factor() 
function but I wonder if it would cause any incompatibility if this was made 
standard. Yes, it costs a little extra in storage.


> ####
  > # PURPOSE: Given a factor that has embedded knowlege of what
  > # the underlying type once was, carefully reconstruct a vector
  > # to put it back where it should have been. If this is a normal R
  > # vector or factor, just return it as of type character.
  > # If an attribute shows the information of what kind it is
  > # was saved, convert it to that.
  > 
  > unfactormem <- function(unorig) {
    +   what_kind <- attr(unorig, "OnceWas")
    +   unorigchar <- as.character(unorig)
    +   if (is.null(what_kind)) return(unorigchar)
    +   as(unorigchar, what_kind)
    + }
>   
  > # Make a logical vector
  > somelogs <- c(TRUE, FALSE, FALSE, TRUE, FALSE)
> 
  > # convert it to factor the new way that saves that it was logical.
  > somelogsfac <- factormem(orig)
Error in factormem(orig) : object 'orig' not found
> 
  > # display what it looks like
  > somelogsfac
[1] TRUE  FALSE FALSE TRUE  FALSE
attr(,"OnceWas")
[1] logical
Levels: FALSE TRUE
> 
  > attributes(somelogsfac)
$levels
[1] "FALSE" "TRUE" 

$class
[1] "factor"

$OnceWas
[1] "logical"

> 
  > attr(somelogsfac, "OnceWas")
[1] "logical"
> 
  > # Revivify it by hand, not the function I made.
  > revived <- as(as.character(somelogsfac), attr(somelogsfac, "OnceWas"))
> 
  > # show what it looks like:
  > revived
[1]  TRUE FALSE FALSE  TRUE FALSE
> 
  > typeof(revived)
[1] "logical"
> 
  > # Alternately, use the function I created to bring it back.
  > 
  > unfactormem(somelogsfac)
[1]  TRUE FALSE FALSE  TRUE FALSE
> 
  > typeof(unfactormem(somelogsfac))
[1] "logical"
> 
  > # A complex example:
  > 
  > somecomplex <- c( 3+5i, 4+6i, 13, 4+6i, 7i, 13)
> typeof(somecomplex)
[1] "complex"
> somecomplexfac <- factormem(somecomplex)
> attributes(somecomplexfac)
$levels
[1] "0+7i"  "3+5i"  "4+6i"  "13+0i"

$class
[1] "factor"

$OnceWas
[1] "complex"

> revivified <- unfactormem(somecomplexfac)
> revivified
[1]  3+5i  4+6i 13+0i  4+6i  0+7i 13+0i
> typeof(revivified)
[1] "complex"


AGAIN, I am not asking this be done as a change in R, just that I think it is 
an idea of how to be able to say undo factorization properly when needed, such 
as before saving it to disk in some data structure, or when passing it for some 
other analysis where a non-factor form would work better.

NOTE: I threw this together quickly and may well have made errors or not made 
it bulletproof. Feel free to point out where I am wrong or how to improve it.

I also note my approach is partially based on  interactions I once had with 
Adrian Dusa when he shared his package "declared" and he needed to maintain 
additional info on some data brought into R that had multiple distinct 
categories of missing data. It had to carefully use attributes but also did 
much more to integrate the functionality more fully. So, yes, that might be 
something that could be done but this is just an academic exercise for me.

-----Original Message-----
From: Bert Gunter <bgunter.4...@gmail.com> 
Sent: Sunday, September 19, 2021 7:19 PM
To: Avi Gross <avigr...@verizon.net>
Cc: Luigi Marongiu <marongiu.lu...@gmail.com>; Rui Barradas 
<ruipbarra...@sapo.pt>; r-help <r-help@r-project.org>
Subject: Re: [R] how to remove factors from whole dataframe?

You do not understand factors. There is no "base type" that can be recovered.

> f <- factor(c(5.1, 6.2), labels = c("whoa","baby")) f
[1] whoa baby
Levels: whoa baby

> unclass(f)
[1] 1 2
attr(,"levels")
[1] "whoa" "baby"

> typeof(f)
[1] "integer"


Bert Gunter

"The trouble with having an open mind is that people keep coming along and 
sticking things into it."
-- Opus (aka Berkeley Breathed in his "Bloom County" comic strip )

On Sun, Sep 19, 2021 at 2:15 PM Avi Gross via R-help <r-help@r-project.org> 
wrote:
>
> Glad we have solutions BUT I note that the more abstract question is how to 
> convert any columns that are factors to their base type and that may well NOT 
> be character. They can be integers or doubles or complex or Boolean and maybe 
> even raw.
>
> So undoing factorization may require using something like typeof() to get the 
> base type and then depending on what final type you have, you may have to do 
> things like as.integer(as.character(the_factor)) to get it as an integer and 
> for a logical, as.logical(factor(c(TRUE, TRUE, FALSE, TRUE, FALSE))) and so 
> on.
>
> This seems like a fairly basic need so I wonder if anyone has already 
> done it. I can see a fairly straightforward way to build a string and 
> use eval and I suspect others might use something else like do.call() 
> and yet others use multiple if statements or a case_when or something
>
>
>
>
> -----Original Message-----
> From: R-help <r-help-boun...@r-project.org> On Behalf Of Luigi 
> Marongiu
> Sent: Sunday, September 19, 2021 4:43 PM
> To: Rui Barradas <ruipbarra...@sapo.pt>
> Cc: r-help <r-help@r-project.org>
> Subject: Re: [R] how to remove factors from whole dataframe?
>
> Awesome, thanks!
>
> On Sun, Sep 19, 2021 at 4:22 PM Rui Barradas <ruipbarra...@sapo.pt> wrote:
> >
> > Hello,
> >
> > Using Jim's lapply(., is.factor) but simplified, you could do
> >
> >
> > df1 <- df
> > i <- sapply(df1, is.factor)
> > df1[i] <- lapply(df1[i], as.character)
> >
> >
> > a one-liner modifying df, not df1 is
> >
> >
> > df[sapply(df, is.factor)] <- lapply(df[sapply(df, is.factor)],
> > as.character)
> >
> >
> > Hope this helps,
> >
> > Rui Barradas
> >
> > Às 11:03 de 19/09/21, Luigi Marongiu escreveu:
> > > Thank you Jim, but I obtain:
> > > ```
> > >> str(df)
> > > 'data.frame': 5 obs. of  3 variables:
> > >   $ region : Factor w/ 5 levels "A","B","C","D",..: 1 2 3 4 5
> > >   $ sales  : num  13 16 22 27 34
> > >   $ country: Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5
> > >> df1<-df[,!unlist(lapply(df,is.factor))]
> > >> str(df1)
> > >   num [1:5] 13 16 22 27 34
> > >> df1
> > > [1] 13 16 22 27 34
> > > ```
> > > I was expecting
> > > ```
> > > str(df)
> > > 'data.frame': 5 obs. of  3 variables:
> > >   $ region : char "A","B","C","D",..: 1 2 3 4 5
> > >   $ sales  : num  13 16 22 27 34
> > >   $ country: char "a","b","c","d",..: 1 2 3 4 5 ```
> > >
> > > On Sun, Sep 19, 2021 at 11:37 AM Jim Lemon <drjimle...@gmail.com> wrote:
> > >>
> > >> Hi Luigi,
> > >> It's easy:
> > >>
> > >> df1<-df[,!unlist(lapply(df,is.factor))]
> > >>
> > >> _except_ when there is only one column left, as in your example. 
> > >> In that case, you will have to coerce the resulting vector back 
> > >> into a one column data frame.
> > >>
> > >> Jim
> > >>
> > >> On Sun, Sep 19, 2021 at 6:18 PM Luigi Marongiu 
> > >> <marongiu.lu...@gmail.com> wrote:
> > >>>
> > >>> Hello,
> > >>> I woul dlike to remove factors from all the columns of a dataframe.
> > >>> I can do it n a column at the time with ```
> > >>>
> > >>> df <- data.frame(region=factor(c('A', 'B', 'C', 'D', 'E')),
> > >>>                   sales = c(13, 16, 22, 27, 34), 
> > >>> country=factor(c('a', 'b', 'c', 'd', 'e')))
> > >>>
> > >>> new_df$region <- droplevels(new_df$region) ```
> > >>>
> > >>> What is the syntax to remove all factors at once (from all columns)?
> > >>> For this does not work:
> > >>> ```
> > >>>> str(df)
> > >>> 'data.frame': 5 obs. of  3 variables:
> > >>>   $ region : Factor w/ 5 levels "A","B","C","D",..: 1 2 3 4 5
> > >>>   $ sales  : num  13 16 22 27 34
> > >>>   $ country: Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5
> > >>>> df = droplevels(df)
> > >>>> str(df)
> > >>> 'data.frame': 5 obs. of  3 variables:
> > >>>   $ region : Factor w/ 5 levels "A","B","C","D",..: 1 2 3 4 5
> > >>>   $ sales  : num  13 16 22 27 34
> > >>>   $ country: Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5 
> > >>> ``` Thank you
> > >>>
> > >>> ______________________________________________
> > >>> R-help@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.
> > >
> > >
> > >
>
>
>
> --
> Best regards,
> Luigi
>
> ______________________________________________
> R-help@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.
>
> ______________________________________________
> R-help@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.

______________________________________________
R-help@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.

Reply via email to