I am attempting to use ifelse to subset data that can then be used in a plot. I am coding it this way as I am trying to make the code usable to a layman by only defining one or two objects and then running the whole script to make a plot using the data selected by given criteria.
The problem is that the mydataframe[mydataframe$data . ...] operation is not working the way I would like it to inside ifelse. Is there a way to get it to work in ifelse or is anyone aware of a smarter way to do what I'm trying to do? Thanks!
Also, the second block of code is added explanation but not needed to see the problem.
# generate data
mydata<-c(1:100)
mydata<-as.data.frame(mydata)
mydata$checkthefunction<-rep(c("One","Two","Three","Four","Multiple of 5",
"Six","Seven","Eight","Nine","Multiple of 10"))
# everything looks right
mydata
# create function
myfunction = function(MyCondition="low"){
# special criteria
lowRandomNumbers=c(58,61,64,69,73)
highRandomNumbers=c(78,82,83,87,90)
# subset the data based on MyCondition
mydata<-ifelse(MyCondition=="low",mydata[mydata$mydata %in% lowRandomNumbers==TRUE,],mydata)
mydata<-ifelse(MyCondition=="high",mydata[mydata$mydata %in% highRandomNumbers==TRUE,],mydata)
# if not "high" or "low" then don't subset the data
mydata
}
myfunction("low")
# returns just the numbers selected from the dataframe, not the
# subsetted dataframe with the $checkthefunction row
myfunction("high")
# returns: "Error in mydata[mydata$mydata %in% highRandomNumbers == TRUE, ] :
# incorrect number of dimensions"
# additional explanation code if it helps
# define dataframe again
mydata<-c(1:100)
mydata<-as.data.frame(mydata)
mydata$checkthefunction<-rep(c("One","Two","Three","Four","Multiple of 5",
"Six","Seven","Eight","Nine","Multiple of 10"))
# outside of the function and ifelse my subsetting works
lowRandomNumbers=c(58,61,64,69,73)
ItWorks<-mydata[mydata$mydata %in% lowRandomNumbers==TRUE,]
# ifelse seems to be the problem, the dataframe is cut into the string of lowRandomNumbers again
MyCondition="low"
NoLuck<-ifelse(MyCondition=="low",mydata[mydata$mydata %in% lowRandomNumbers==TRUE,],mydata)
NoLuck
# if the 'else' portion is returned the dataframe is converted to a one-dimensional list
MyCondition="high"
NoLuck<-ifelse(MyCondition=="low",mydata[mydata$mydata %in% lowRandomNumber==TRUE,mydata)
NoLuck
You don't want ifelse. You want if and else. ifelse is used if you have a condition vector. You only have a single condition value.
myfunction = function(MyCondition="low"){
# special criteria
lowRandomNumbers=c(58,61,64,69,73)
highRandomNumbers=c(78,82,83,87,90)
# subset the data based on MyCondition
mydata <- if(MyCondition=="low") mydata[mydata$mydata %in% lowRandomNumbers==TRUE,] else mydata
mydata <- if(MyCondition=="high") mydata[mydata$mydata %in% highRandomNumbers==TRUE,] else mydata
# if not "high" or "low" then don't subset the data
mydata
}
Related
I have a data set with Air Quality Data. The Data Frame is a matrix of 153 rows and 5 columns.
I want to find the mean of the first column in this Data Frame.
There are missing values in the column, so I want to exclude those while finding the mean.
And finally I want to do that using Control Structures (for loops and if-else loops)
I have tried writing code as seen below. I have created 'y' instead of the actual Air Quality data set to have a reproducible example.
y <- c(1,2,3,NA,5,6,NA,NA,9,10,11,NA,13,NA,15)
x <- matrix(y,nrow=15)
for(i in 1:15){
if(is.na(data.frame[i,1]) == FALSE){
New.Vec <- c(x[i,1])
}
}
print(mean(New.Vec))
I expected the output to be the mean. Though the error I received is this:
Error: object 'New.Vec' not found
One line of code, no need for for loop.
mean(data.frame$name_of_the_first_column, na.rm = TRUE)
Setting na.rm = TRUE makes the mean function ignore NAs.
Here, we can make use of na.aggregate from zoo
library(zoo)
df1[] <- na.aggregate(df1)
Assuming that 'df1' is a data.frame with all numeric columns and wanted to fill the NA elements with the corresponding mean of that column. na.aggregate, by default have the fun.aggregate as mean
can't see your data, but probably like this? the vector needed to be initialized. better to avoid loops in R when you can...
myDataFrame <- read.csv("hw1_data.csv")
New.Vec <- c()
for(i in 1:153){
if(!is.na(myDataFrame[i,1])){
New.Vec <- c(New.Vec, myDataFrame[i,1])
}
}
print(mean(New.Vec))
I'm trying to change column names over multiple data sets. I have tried writing the following function to do this:
# simplified test data #
df1<-as.data.frame(c("M","F"))
colnames(df1)<-"M1"
# my function #
rename_cols<-function(df){
colnames(df)[names(df) == "M1"] <- "sex"
}
rename_cols(df1)
However when testing this function on df1, the column is always called "M1" instead of "sex". How can I correct this?
SOLUTION - THANKS TO DAVID ARENBERG
rename_cols<-function(df){
colnames(df)[names(df) == "M1"] <- "sex"
df
}
df1<-rename_cols(df1)
Here is another solution which gets around the problem of functions operating in a temporary space:
df <- as.data.frame(c("M","F"))
colnames(df) <- "M1"
rename_cols <- function(df) {
colnames(df)[names(df) == "M1"] <<- "sex"
}
> rename_cols(df) # this will operate directly on the 'df' object
> df
sex
1 M
2 F
Using the global assignment operator <<- makes the name changes to the input data frame df "stick". Granted, this solution is not ideal because it means the function could potentially do something unwanted. But I feel this is in the spirit of what you were trying to do originally.
I have two data frames of different length, and I want to add a new column to the first data frame with corresponding values of the second data frame.
The corresponding value is defined by the following condition if (DF1[i,1] == DF2[,1] & DF1[i,2] == DF2[i,2]) == TRUE, then the value of this row should be taken from DF2 and written to DF1$newColumn[i].
The following data frames are used to illustrate the question:
DF1<-data.frame(X = rep(c("A","B","C"),each=3),
Y = rep(c("a","b","c"),each=3))
DF2<-data.frame(X = c("A","B","C"),
Y = c("a","b","c"),
Z = c(1:3))
I tried to use if() statements as in the text above but the condition returns a vector of TRUE/FALSE and that doesn't seem to work.
The code that works that I use now is
for (i in 1 : length(DF1[,1])) {
DF1$Z[i] <- subset(DF2,DF2$X == DF1$X[i] & DF2$Y == DF1$Y[i])$Z
}
However it is incredibly slow (user system elapsed 115.498 12.341 127.799 for my full dataframe) and there must be a more efficient way to code this. Also, I have read repeatedly that vectorizing is more efficient then loops but I don't know how to do that.
I do need to work with conditional statements though so something like
DF1$Zz<-rep(DF2$Z,each=3)
wouldn't work for my real dataset.
DF1$Z <- sapply(1:nrow(DF1), function(i) DF2$Z[DF2$X==DF1$X[i] & DF2$Y==DF1$Y[i]]) seems to be taking roughly a quarter of the time of your for loop.
I created DF1 with 300 each reps, and my function took ~2secs to run; your loop with subset took ~8secs to run, and repackaging your loop into an sapply it took ~5secs to run.
I have a large dataset called "inputs". One of the columns in the dataset is a flag called "constrained" with either "Y" or "N". I want to create two datasets where one is the rows where the flag is "Y" and one is the rows where the flag is "N".
I tried:
ifelse(inputs$constrained == "N",unconstrained <- inputs,constrained <- inputs)
but both datasets unconstrained and constrained are identical to inputs.
What am I doing wrong?
first <- split(inputs, inputs$constrained)[1]
second <- split(inputs, inputs$constrained)[2]
If you wanted to use "[" you could do this:
unconstrd <- inputs[ inputs$constrained == "N" , ]
constrd <- inputs[ ! inputs$constrained == "N" , ]
Both of that second option might have entries where 'constrained' is NA, due the screwy way that R handles NA conditionals although it would not be a faithful reflection of those rows. (I admit I did not sure what the split method does with NA's.) I just tested the split method and it might be superior, since (like subset) it does not return the is.na(input$constrained) rows.
I'm still new to R and do all of my subsetting via the pattern:
data[ command that produces logical with same length as data ]
or
subset( data , command that produces logical with same length as data )
for example:
test = c("A", "B","C")
ignore = c("B")
result = test[ !( test %in% ignore ) ]
result = subset( test , !( test %in% ignore ) )
But I vaguely remember from my readings that there's a shorter/(more readable?) way to do this? Perhaps using the "with" function?
Can someone list alternative to the example above to help me understand the options in subsetting?
I don't know of a more succinct way of subsetting for your specific example, using only vectors. What you may be thinking of, regarding with, is subsetting data frames based on conditions using columns from that data frame. For example:
dat <- data.frame(variable1 = runif(10), variable2 = letters[1:10])
If we want grab a subset of dat based on a condition using variable1 we could do this:
dat[dat$variable1 < 0,]
or we can save ourselves having to write dat$* each time by using with:
with(dat,dat[variable1 < 0,])
Now, you'll notice that I really didn't save any keystrokes by doing that in this case. But if you have a data frame with a long name, and a complicated condition it can save you a bit. See also the related ?within command if you're altering the data frame in question.
Alternatively, you can use subset which can do essentially the same thing:
subset(dat, variable1 < 0)
subset can also handle conditions on the columns via the select argument.
The with function would help if test were a column in a data frame (or object in a list), but with global vectors with does not help.
Some people have created a not in operator that could save a couple of key strokes from what you did. If all the values in test are unique then the setdiff function may be what you are thinking of (but if for example you had multiple "A"s then setdiff would only return 1 of them).
With your ignore being only 1 value you could use test != ignore, but that does not generalize to ignore having 2 or more values.
I have seen timed comparisons of alternate methods and %in% (based on match) was one of the best performing strategies.
Alternates:
test[!test=="B"] #logical indexing
test[which(test != "B")] #numeric indexing
# the which() is not superfluous when there are NA's if you want them ignored
Another alternative to the original example:
test[test != ignore]
Other ways, using joran's example:
set.seed(1)
df <- data.frame(variable1 = runif(10), variable2 = letters[1:10])
Returning one column: df[[1]]. df$name is equivalent to df[["name", exact = FALSE]]
df[df[[1]] < 0.5, ]
df[df["variable1"] < 0.5, ]
Returning one data frame of one column: df[1]
df[df[1] < 0.5, ]
Using with
with(df, df[df[[1]] < 0.5, ]) # One column
with(df, df[df["variable1"] < 0.5, ]) # One column
with(df, df[df[1] < 0.5, ]) # data frame of one column
Using dplyr:
library(dplyr)
filter(df, variable1 < 0.5)