Plotly Histogram Retains Ghost Categorical X Values - r

I have data with fifty various categorical values in a column labelled "cat", and a second column with a continuous numerical value "amount". I only want to plot the subset of "cat" with an "amount" greater than 5. Why do I have the ghost-label on my x-axis for those intermediate rows that should be omitted based on my subset?
Example code:
cat<-c("a","b","c","d","e")
amount<-c(4,15,18,2,9)
df<-data.frame(cat=cat,amount=amount)
df1<-subset(df,amount >5)
library(plotly)
p <- plot_ly(df1, x = ~cat, y = ~amount)
p
df1 printed out:
cat amount
2 b 15
3 c 18
5 e 9
And the plot generated:
It is interesting that "a" doesn't appear on my x axis, but "d" does. I take it there is something going on with the row numbers, but why is this and how can I prevent this from happening?
Thank you in advance.

subset does not drop the unused levels of a factor as shown below
str(df1)
'data.frame': 3 obs. of 2 variables:
$ cat : Factor w/ 5 levels "a","b","c","d",..: 2 3 5
$ amount: num 15 18 9
So stringsAsFactors = FALSE will import cat as a character vector which you can modify to factor after subsetting or use it directly.
df <- data.frame(cat=cat,amount=amount, stringsAsFactors = FALSE)
df1 <- subset(df,amount >5)
plot_ly(df1, x = ~cat, y = ~amount)

Related

How can I convert a numeric vector with discreete value ranges into a factor?

I am trying to convert a numeric vector with discreete values into a factor in R.
x <- c(1,2,3,4,8,9,10,88,89,90)
I need this vector to be converted into a factor variable with 4 levels as follows:
1,2 (level 1)
3,4 (level 2)
8,9,10 (level 3)
88,89, 90 (level 4)
I have tried using factor in R as follows:
y <- factor(x, levels = c(1:2, 3:4, 8:10, 88:90))
This returns a factor with 10 levels instead of a factor with 4 levels that I want.
str(y)
Factor w/ 10 levels "1","2","3","4",..: 1 2 3 4 5 6 7 8 9 10
I have also tried using cut as follows:
bins <- c(1,3,5,8,11,88,90)
y <- cut(x, breaks = bins, right = F, include.lowest = T)
table(y)
This also does not return the desired result as it creates a level for ranges such as [5-8) and [11-88) that I dont need.
y
[1,3) [3,5) [5,8) [8,11) [11,88) [88,90]
2 2 0 3 0 3
Is there a way to convert a range of numeric values into a factor in R?
Drop unused levels:
# as per your code
bins <- c(1,3,5,8,11,88,90)
y <- cut(x, breaks = bins, right = FALSE, include.lowest = TRUE)
levels(y)
# [1] "[1,3)" "[3,5)" "[5,8)" "[8,11)" "[11,88)" "[88,90]"
# drop unused levels
y1 <- droplevels(y)
levels(y1)
#[1] "[1,3)" "[3,5)" "[8,11)" "[88,90]"
We can use case_when
library(dplyr)
case_when(x %in% 1:2 ~ 1, x %in% 3:4 ~ 2, x %in% 8:10 ~ 3, x%in% 88:90 ~ 4)

How to change factor arguments? (R)

Here are three vectors.
vec1 <- 1:6
vec2 <- c('radio', 'newspaper', 'web-page', 'chat', 'tv', 'web-page')
vec3 <- c(0, 0, 1, 1, 0, 1)
The task is to form a data frame with the following structure using these vectors.
'data.frame': 6 obs. of 3 variables:
$ id : int 1 2 3 4 5 6
$ response: Factor w/ 2 levels "No","Yes": 1 1 2 2 1 2
$ medium : chr "radio" "newspaper" "web-page" "chat" ...
Here is my solution.
dfr <- data.frame(id = vec1, response = vec3, medium = vec2, stringsAsFactors = FALSE)
dfr$response <- factor(x = , levels = , labels = )
My question is: "What values should the arguments (x, levels, labels) have and why?"
Talking about this line:
dfr$response <- factor(x = , levels = , labels = )
We can assign labels to vec3 as levels are by default taken from unique values of vec3.
df <- data.frame(id = vec1, response = factor(vec3, labels = c('No', 'Yes')),
medium = vec2, stringsAsFactors = FALSE)
str(df)
#'data.frame': 6 obs. of 3 variables:
#$ id : int 1 2 3 4 5 6
#$ response: Factor w/ 2 levels "No","Yes": 1 1 2 2 1 2
#$ medium : chr "radio" "newspaper" "web-page" "chat" ...
You can read ?factor for more details.
In this:
x is the vector of data that you want to turn into a factor, in this case the responses x=df$response
Levels is a vector of values that x might have taken. The default is a list of the distinct values of x, in ascending order (numeric or alphabetical), so the default would be c(0, 1). You don't need to include the levels, as it will automatically detect them, however as you're adding labels then it's good practice to add the levels so your labels match up in case you have lots of levels and manage to get the order mixed up.
Labels can either be a single string or a vector of all labels for the levels, you can use labels to map multiple values to the same Label. For your task you would use c("No", "Yes"). the default for Labels is the levels i.e. no label.
So your final code will be
dfr$response <- factor(x=dfr$response, levels=c(0,1), labels=c("No", "Yes"))
As a minor aside, people generally use df to represent a data frame, rather than dfr. It doesn't make any difference, but is just the commonly used notation.

BNlearn R error “variable Variable1 must have at least two levels.”

Trying to create a BN using BNlearn, but I keep getting an error;
Error in check.data(data, allowed.types = discrete.data.types) : variable Variable1 must have at least two levels.
It gives me that error for every of my variable, even though they're all factors and has more than 1 levels, As you can see - in this case my variable "model" has 4 levels
As I can't share the variables and dataset, I've created a small set and belonging code to the data set. I get the same problem. I know I've only shared 2 variables, but I get the same error for all the variables.
library(tidyverse)
library (bnlearn)
library(openxlsx)
DataFull <- read.xlsx("(.....)/test.xlsx", sheet = 1, startRow = 1, colNames = TRUE)
set.seed(600)
DataFull <- as_tibble(DataFull)
DataFull$Variable1 <- as.factor(DataFull$Variable1)
DataFull$TargetVar <- as.factor(DataFull$TargetVar)
DataFull <- na.omit(DataFull)
DataFull <- droplevels(DataFull)
DataFull <- DataFull[sample(nrow(DataFull)),]
Data <- DataFull[1:as.integer(nrow(DataFull)*0.70)-1,]
Datatest <- DataFull[as.integer(nrow(DataFull)*0.70):nrow(DataFull),]
nrow(Data)+nrow(Datatest)==nrow(DataFull)
FocusVar <- as.character("TargetVar")
BN.naive <- naive.bayes(Data, FocusVar)
Using str(data), I can see that the variable has 2 or more levels already:
str(Data)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 27586 obs. of 2 variables:
$ Variable1: Factor w/ 3 levels "Small","Medium",..: 2 2 3 3 3 3 3 3 3 3 ...
$ TargetVar: Factor w/ 2 levels "Yes","No": 1 1 1 1 1 1 2 1 1 1 ...
Link to data set: https://drive.google.com/open?id=1VX2xkPdeHKdyYqEsD0FSm1BLu1UCtOj9eVIVfA_KJ3g
bnlearn expects a data.frame : doesn't work with tibbles, So keep your data as a data.frame by omitting the line DataFull <- as_tibble(DataFull)
Example
library(tibble)
library (bnlearn)
d <- as_tibble(learning.test)
hc(d)
Error in check.data(x) : variable A must have at least two levels.
In particular, it is the line from bnlearn:::check.data
if (nlevels(x[, col]) < 2)
stop("variable ", col, " must have at least two levels.")
In a standard data.frame,learning.test[,"A"] returns a vector and so nlevels(learning.test[,"A"]) works as expected, however, by design, you cannot extract vectors like this from tibbles : d[,"A"]) is still a tbl_df and not a vector hence nlevels(d[,"A"]) doesn't work as expected, and returns zero.

R, generating dataframe row from factor values

I have an R data frame with factor columns.
dataframe <- read.csv("import.csv")
dataframe$col1 = as.factor(dataframe$col1)
dataframe$col2 = as.factor(dataframe$col2)
...
How can I generate a new row from labels?
newRow = dataframe[1,] #template
newRow[1] = ?
newRow[2] = ?
Lets say col1 includes "TestValue". I would like to set newRow[1] value to "TestValue" as if it was selected from my dataframe. How can I do that?
I know I can get factor index like so:
match(c("TestValue"),levels(dataframe$col1))
[1] 3
But whenever I assign anything to newRow[1], I seem to change its type.
Thanks in advance.
You could assign a factor to newRow[1] and maintain the levels too.
In your case:
newRow[1] <- factor('TestValue', levels = levels(df$col1))
As an example:
df <- data.frame(a = letters, b = letters)
new <- df[1, ]
new[1] <- factor('b', levels = levels(df[[1]]))
Output:
> str(new)
'data.frame': 1 obs. of 2 variables:
$ a: Factor w/ 26 levels "a","b","c","d",..: 2
$ b: Factor w/ 26 levels "a","b","c","d",..: 1
column a is still a factor with all the levels

Convert factor to integer in a data frame

I have the following code
anna.table<-data.frame (anna1,anna2)
write.table<-(anna.table, file="anna.file.txt",sep='\t', quote=FALSE)
my table in the end contains numbers such as the following
chr start end score
chr2 41237927 41238801 151
chr1 36976262 36977889 226
chr8 83023623 83025129 185
and so on......
after that i am trying to to get only the values which fit some criteria such as score less than a specific value
so i am doing the following
anna3<-"data/anna/anna.file.txt"
anna.total<-read.table(anna3,header=TRUE)
significant.anna<-subset(anna.total,score <=0.001)
Error: In Ops.factor(score, 0.001) <= not meaningful for factors
so i guess the problem is that my table has factors and not integers
I guess that my anna.total$score is a factor and i must make it an integer
If i read correctly the as.numeric might solve my problem
i am reading about the as.numeric function but i cannot understand how i can use it
Hence could you please give me some advices?
thank you in advance
best regards
Anna
PS : i tried the following
anna3<-"data/anna/anna.file.txt"
anna.total<-read.table(anna3,header=TRUE)
anna.total$score.new<-as.numeric (as.character(anna.total$score))
write.table(anna.total,file="peak.list.numeric.v3.txt",append = FALSE ,quote = FALSE,col.names =TRUE,row.names=FALSE, sep="\t")
anna.peaks<-subset(anna.total,fdr.new <=0.001)
Warning messages:
1: In Ops.factor(score, 0.001) : <= not meaningful for factors
again i have the same problem......
With anna.table (it is a data frame by the way, a table is something else!), the easiest way will be to just do:
anna.table2 <- data.matrix(anna.table)
as data.matrix() will convert factors to their underlying numeric (integer) levels. This will work for a data frame that contains only numeric, integer, factor or other variables that can be coerced to numeric, but any character strings (character) will cause the matrix to become a character matrix.
If you want anna.table2 to be a data frame, not as matrix, then you can subsequently do:
anna.table2 <- data.frame(anna.table2)
Other options are to coerce all factor variables to their integer levels. Here is an example of that:
## dummy data
set.seed(1)
dat <- data.frame(a = factor(sample(letters[1:3], 10, replace = TRUE)),
b = runif(10))
## sapply over `dat`, converting factor to numeric
dat2 <- sapply(dat, function(x) if(is.factor(x)) {
as.numeric(x)
} else {
x
})
dat2 <- data.frame(dat2) ## convert to a data frame
Which gives:
> str(dat)
'data.frame': 10 obs. of 2 variables:
$ a: Factor w/ 3 levels "a","b","c": 1 2 2 3 1 3 3 2 2 1
$ b: num 0.206 0.177 0.687 0.384 0.77 ...
> str(dat2)
'data.frame': 10 obs. of 2 variables:
$ a: num 1 2 2 3 1 3 3 2 2 1
$ b: num 0.206 0.177 0.687 0.384 0.77 ...
However, do note that the above will work only if you want the underlying numeric representation. If your factor has essentially numeric levels, then we need to be a bit cleverer in how we convert the factor to a numeric whilst preserving the "numeric" information coded in the levels. Here is an example:
## dummy data
set.seed(1)
dat3 <- data.frame(a = factor(sample(1:3, 10, replace = TRUE), levels = 3:1),
b = runif(10))
## sapply over `dat3`, converting factor to numeric
dat4 <- sapply(dat3, function(x) if(is.factor(x)) {
as.numeric(as.character(x))
} else {
x
})
dat4 <- data.frame(dat4) ## convert to a data frame
Note how we need to do as.character(x) first before we do as.numeric(). The extra call encodes the level information before we convert that to numeric. To see why this matters, note what dat3$a is
> dat3$a
[1] 1 2 2 3 1 3 3 2 2 1
Levels: 3 2 1
If we just convert that to numeric, we get the wrong data as R converts the underlying level codes
> as.numeric(dat3$a)
[1] 3 2 2 1 3 1 1 2 2 3
If we coerce the factor to a character vector first, then to a numeric one, we preserve the original information not R's internal representation
> as.numeric(as.character(dat3$a))
[1] 1 2 2 3 1 3 3 2 2 1
If your data are like this second example, then you can't use the simple data.matrix() trick as that is the same as applying as.numeric() directly to the factor and as this second example shows, that doesn't preserve the original information.
I know this is an older question, but I just had the same problem and may be it helps:
In this case, your score column seems like it should not have become a factor column. That usually happens after read.table when it is a text column. Depending on which country you are from, may be you separate floats with a "," and not with a ".". Then R thinks that is a character column and makes it a factor. AND in that case Gavins answer won't work, because R won't make "123,456" to 123.456 . You can easily fix that in a text editor with replace "," with "." though.

Resources