Subsetting R with dynamic variables [duplicate] - r

This question already has answers here:
Brackets make a vector different. How exactly is vector expression evaluated?
(3 answers)
Closed 6 years ago.
I have the below example code. I have a dataframe ts which has 16 rows. when I subset with actual numbers it works fine but when I subset with calculated numbers why is my code behaving weirdly ?
Can anyone please explain me what's wrong in this?
Case1:
> a
[1] 12
> c
[1] 16
> ts$trend[13:16]
[1] 21.36926 21.48654 21.60383 21.72111
> ts$trend[a+1:c]
[1] 21.36926 21.48654 21.60383 21.72111 NA NA NA NA NA NA NA NA
[13] NA NA NA NA
Case 2:
> b
[1] 4
> temp[1: 8]
[1] 1 2 3 4 5 6 7 8
> temp[1: b+b]
[1] 5 6 7 8

R doesn't care about they way you space expressions. Things are evaluated according to a strict precedence scheme. Things in parentheses are done first. So:
> 1: b+b
[1] 5 6 7 8
because addition has lower precedence than ":". The 1:b is evaluated first, and then b is added. So you get:
> (1:b)+b
[1] 5 6 7 8
If you want the alternative, parenthesise things:
> 1:(b+b)
[1] 1 2 3 4 5 6 7 8
I'd suggest you also parenthesise (1+b):b if that is ever what you want - the brackets make no difference but they aid readability for anyone who forgets the precedence rules.

This is a case of operator precedence. It can be avoided by using brackets
temp[1:(b+b)]
#[1] 1 2 3 4 5 6 7 8
If we check the problem in OP's code
1:b
#[1] 1 2 3 4
(1:b) + b
#[1] 5 6 7 8
So, the operator precedence happens here by evaluating 1:b followed by adding the b.
This is well described in ?Syntax
:: ::: access variables in a namespace
$ # component / slot
extraction [ [[ indexing
^ exponentiation (right to left)
- + unary minus and plus
: sequence operator %any% special operators (including %% and %/%)
* / multiply, divide
+ - (binary) add, subtract
< > <= >= == != ordering and comparison
! negation
& && and
| || or
~ as in formulae
-> ->> rightwards assignment
<- <<- assignment (right to left)
= assignment (right to left)
? help (unary and binary)
data
temp <- 1:10
b <- 4

Related

How does is.null work on list elements in R? [duplicate]

I found a very suprising and unpleasant feature of R - it completes list item names!!! See the following code:
a <- list(cov_spring = "spring")
a$cov <- c()
a$cov
# spring ## I expect it to be empty!!! I've set it empty!
a$co
# spring
a$c
I don't know what to do with that.... I need to be able to set $cov to NULL and have $cov_spring there at the same time!!! And use $cov separately!! This is annoying!
My question:
What is going on here? How is this possible, what is the logic behind?
Is there some easy fix, how to turn this completion off? I need to use list items cov_spring and cov independently as if they are normal variables. No damn completion please!!!
From help("$"):
'x$name' is equivalent to 'x[["name", exact = FALSE]]'
When you scroll back and read up on exact=:
exact: Controls possible partial matching of '[[' when extracting by
a character vector (for most objects, but see under
'Environments'). The default is no partial matching. Value
'NA' allows partial matching but issues a warning when it
occurs. Value 'FALSE' allows partial matching without any
warning.
So this provides you partial matching capability in both $ and [[ indexing:
mtcars$cy
# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
mtcars[["cy"]]
# NULL
mtcars[["cy", exact=FALSE]]
# [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
There is no way I can see of to disable the exact=FALSE default for $ (unless you want to mess with formals, which I do not recommend for the sake of reproducibility and consistent behavior).
Programmatic use of frames and lists (for defensive purposes) should prefer [[ over $ for precisely this reason. (It's rare, but I have been bitten by this permissive behavior.)
Edit:
For clarity on that last point:
mtcars$cyl becomes mtcars[["cyl"]]
mtcars$cyl[1:3] becomes mtcars[["cyl"]][1:3]
mtcars[,"cy"] is not a problem, nor is mtcars[1:3,"cy"]
You can use [ or [[ instead.
a["cov"] will return a list with a NULL element.
a[["cov"]] will return the NULL element directly.

Strings in datatable (imported from database) get coerced into integers? [duplicate]

This question already has answers here:
How to convert a factor to integer\numeric without loss of information?
(12 answers)
Closed 6 years ago.
I've imported a test file and tried to make a histogram
pichman <- read.csv(file="picman.txt", header=TRUE, sep="/t")
hist <- as.numeric(pichman$WS)
However, I get different numbers from values in my dataset. Originally I thought that this because I had text, so I deleted the text:
table(pichman$WS)
ws <- pichman$WS[pichman$WS!="Down" & pichman$WS!="NoData"]
However, I am still getting very high numbers does anyone have an idea?
I suspect you are having a problem with factors. For example,
> x = factor(4:8)
> x
[1] 4 5 6 7 8
Levels: 4 5 6 7 8
> as.numeric(x)
[1] 1 2 3 4 5
> as.numeric(as.character(x))
[1] 4 5 6 7 8
Some comments:
You mention that your vector contains the characters "Down" and "NoData". What do expect/want as.numeric to do with these values?
In read.csv, try using the argument stringsAsFactors=FALSE
Are you sure it's sep="/t and not sep="\t"
Use the command head(pitchman) to check the first fews rows of your data
Also, it's very tricky to guess what your problem is when you don't provide data. A minimal working example is always preferable. For example, I can't run the command pichman <- read.csv(file="picman.txt", header=TRUE, sep="/t") since I don't have access to the data set.
As csgillespie said. stringsAsFactors is default on TRUE, which converts any text to a factor. So even after deleting the text, you still have a factor in your dataframe.
Now regarding the conversion, there's a more optimal way to do so. So I put it here as a reference :
> x <- factor(sample(4:8,10,replace=T))
> x
[1] 6 4 8 6 7 6 8 5 8 4
Levels: 4 5 6 7 8
> as.numeric(levels(x))[x]
[1] 6 4 8 6 7 6 8 5 8 4
To show it works.
The timings :
> x <- factor(sample(4:8,500000,replace=T))
> system.time(as.numeric(as.character(x)))
user system elapsed
0.11 0.00 0.11
> system.time(as.numeric(levels(x))[x])
user system elapsed
0 0 0
It's a big improvement, but not always a bottleneck. It gets important however if you have a big dataframe and a lot of columns to convert.

R object of data.frame and data.table have same type?

I am still very new to R and recently came across something I am not sure what it means. data.frame and data.table have same type? Can an object have multiple types? After converting "cars" from data.frame to data.table, I obviously can't apply functions that apply to data.frames and not data.table, but class() shows the "cars" is still a data.frame. Anyone know why?
> class(cars)
[1] "data.frame"
> cars<-data.table(cars)
> class(cars)
[1] "data.table" "data.frame"
It is not clear what you mean by your line "I obviously can't apply functions that apply to data.frames and not data.table".
Many functions work as you would expect, whether applied to a data.frame or to a data.table. In particular, if you read the help page to ?data.table, you would find this specific line in the first paragraph of the description:
Since a data.table is a data.frame, it is compatible with R functions and packages that only accept data.frame.
You can test this out yourself:
library(data.table)
CARS <- data.table(cars)
The following should all give you the same results. They aren't the "data.table" way of doing things, but I've just popped off a few things off the top of my head to show you that many (most?) functions can be used with data.table the same way that you would use them with data.frame (but at that point, you miss out on all the great stuff that data.table has to offer).
with(cars, tapply(dist, speed, FUN = mean))
with(CARS, tapply(dist, speed, FUN = mean))
aggregate(dist ~ speed, cars, as.vector)
aggregate(dist ~ speed, CARS, as.vector)
colSums(cars)
colSums(CARS)
as.matrix(cars)
as.matrix(CARS)
t(cars)
t(CARS)
table(cut(cars$speed, breaks=3), cut(cars$dist, breaks=5))
table(cut(CARS$speed, breaks=3), cut(CARS$dist, breaks=5))
cars[cars$speed == 4, ]
CARS[CARS$speed == 4, ]
However, there are some cases in which this won't work. Compare:
cars[cars$speed == 4, 1]
CARS[CARS$speed == 4, 1]
For a better understanding of that, I recommend reading the FAQs. In particular, a couple of relevant points have been summarized at this question: what you can do with data.frame that you can't in data.table.
If your question is, more generally, "Can an object have more than one class?", then you've seen from your own exploration that, yes, it can. For more about that, you can read this page from Hadley's devtools wiki.
Classes also affect things like how objects are printed and how they interact with other functions.
Consider the rle function. If you look at the class, it returns "rle", and if you look at its structure, it shows that it is a list.
> x <- rev(rep(6:10, 1:5))
> y <- rle(x)
> x
[1] 10 10 10 10 10 9 9 9 9 8 8 8 7 7 6
> y
Run Length Encoding
lengths: int [1:5] 5 4 3 2 1
values : int [1:5] 10 9 8 7 6
> class(y)
[1] "rle"
> str(y)
List of 2
$ lengths: int [1:5] 5 4 3 2 1
$ values : int [1:5] 10 9 8 7 6
- attr(*, "class")= chr "rle"
As the length of each list item is the same, you might expect that you can conveniently use data.frame() to convert it to a data.frame. Let's try:
> data.frame(y)
Error in as.data.frame.default(x[[i]], optional = TRUE, stringsAsFactors = stringsAsFactors) :
cannot coerce class ""rle"" to a data.frame
> unclass(y)
$lengths
[1] 5 4 3 2 1
$values
[1] 10 9 8 7 6
> data.frame(unclass(y))
lengths values
1 5 10
2 4 9
3 3 8
4 2 7
5 1 6
Or, let's add another class to the object and try:
> class(y) <- c(class(y), "list")
> y ## Printing is not affected
Run Length Encoding
lengths: int [1:5] 5 4 3 2 1
values : int [1:5] 10 9 8 7 6
> data.frame(y) ## But interaction with other functions is
lengths values
1 5 10
2 4 9
3 3 8
4 2 7
5 1 6
Data.table and data.frame are different classes, but they are related through inheritance. Data.table inherits from data.frame, and basically expands its capabilities. You can also see that after converting cars to the data.table class:
R> typeof(cars)
[1] "list" # similar to dataframe
R> mode(cars)
[1] "list" # idem
More information here or just google for "inheritance".

returning a list in R and functional programming behavior

I have a basic questions regarding functional programming in R.
Given a function that returns a list, such as:
myF <- function(x){
return (list(a=11,b=x))
}
why is it that the list returned when calling the function with a range or vector is always the same lenght for 'a'
Ex:
myF(1:10)
returns:
$a
[1] 11
$b
[1] 1 2 3 4 5 6 7 8 9 10
How can one change the behavior so that the 'a' list has the sample length as b's.
I am actually working with a bunch of S4 objects that do I cannot easily convert to list (using as.list) so _apply is not my first choice.
Thanks for any insight or help!
EDIT (Added further explanations)
I am not necessarily looking to just pad 'a' to makes its length equal to b's. However using the solution
as.list(data.frame(a=myA,b=x)) pads the 'a' with the same value computed first.
myF <- function(x){
myA = ceiling(runif(1, max=100))
return (as.list(data.frame(a=myA
,b=x)))
}
myF(1:5)
$a
[1] 79 79 79 79 79 79 79 79 79 79
$b
[1] 1 2 3 4 5 6 7 8 9 10
I still am not sure why that happens!
Thanks
are you just looking to have 11 repeated so that a is the same length as b? if so:
> myF <- function(x){
+ return (list(a=rep(11,length(x)),b=x))
+ }
> myF(1:10)
$a
[1] 11 11 11 11 11 11 11 11 11 11
$b
[1] 1 2 3 4 5 6 7 8 9 10
EDIT based on OP's clarification/comments. If you want 'a' to instead be a random vector with length equal to 'b':
> myF <- function(x){
+ return (list(a=ceiling(runif(length(x),max=100)),b=x))
+ }
> myF(1:10)
$a
[1] 4 31 8 45 25 74 36 95 64 32
$b
[1] 1 2 3 4 5 6 7 8 9 10
I don't quite understand what you mean by not being able to use as.list. You should be able to get a version of your function satisfying the requirement that all components of the list be equally long by doing:
myF <- function(x){
return as.list(data.frame(a=11,b=x))
}
EDIT:
The reason list does not work the way you expect is that list applied to a number of lists/vectors/e.t.c. is just that, a list of those lists/vectors/e.t.c.; it does not "inspect" their structure.
What I think you want is the additional semantics that the vectors contained in the list should match up and produce a set of "rows", each with one corresponding element from each one of your vectors. This is exactly what a data frame is suppose to be (indeed how, I think, a data frame is represented in R). The final as.list call does little but change what type its tagged as.
EDIT2:
Note that if I'm wrong above (and that's not the general behaviour you want) then Mac's solution is more appropriate, as it gives you exactly the behaviour that both the vectors should have the same length, without implying that they should "line up".
This would both be confusing to anyone reading the code (as using a data.frame implies you think of your vectors as matching up) as well as forcing any additional elements you add to the list to be converted into vectors of the appropriate length (which may or may not be what you want)
In case I did not understand you correctly last time, here is another possibility:
If you want to generate a second vector, given some function/expression, of the same length as your argument you could do something like:
myF <- function(x){
return (list(a=replicate(length(x),f),b=x))
}
in your example f could be runif(1, max=100), though in the specific case of runif you could explicitly tell it to generate a vector of appropriate length by calling runif(length(x), max=100) inside the function.
replicate simply re-evaluates f the number of times you request, and gives you the vector of all the results.
It appears that your function is "hard coding" a. So no matter what you specify it will always give 11.
If for example you changed the function to:
myF <- function(x){ return (list(a=x,b=x)) }
myF(1:10)
$a
[1] 1 2 3 4 5 6 7 8 9 10
$b
[1] 1 2 3 4 5 6 7 8 9 10
a is allowed to change like b.
or
myF <- function(x,y){ return (list(a=y,b=x)) }
myF(10:1,1:10)
$a
[1] 1 2 3 4 5 6 7 8 9 10
$b
[1] 10 9 8 7 6 5 4 3 2 1
Now a is allowed to change independent of b.

loop over columns with semi like columnnames

I have the following variable and dataframe
welltypes <- c("LC","HC")
qccast <- data.frame(
LC_mean=1:10,
HC_mean=10:1,
BC_mean=rep(0,10)
)
Now I only want to see the welltypes I selected(in this case LC and HC, but it could also be different ones.)
for(i in 1:length(welltypes)){
qccast$welltypes[i]_mean
}
This does not work, I know.
But how do i loop over those columns?
And it has to happen variable wise, because welltypes is of an unkown size.
The second argument to $ needs to be a column name of the first argument. I haven't run the code, but I would expect welltypes[i]_mean to be a syntax error. $ is similar to [[, so you can use paste to create the column name string and subset via [[.
For example:
qccast[[paste(welltypes[i],"_mean",sep="")]]
Depending on the rest of your code, you may be able to do something like this instead.
for(i in paste(welltypes,"_mean",sep="")){
qccast[[i]]
}
Here's another strategy:
qccast[ sapply(welltypes, grep, names(qccast)) ]
LC_mean HC_mean
1 1 10
2 2 9
3 3 8
4 4 7
5 5 6
6 6 5
7 7 4
8 8 3
9 9 2
10 10 1
Another easy way to access given welltypes
qccast[,paste(welltypes, '_mean', sep = "")]

Resources