Easy assignments with empty square brackets? x[]&l

2020-04-02 08:38发布

问题:

While looking at an answer posted recently on SO, I noticed an unfamiliar assignment statement. Instead of the usual form of myVar<- myValue , it used the for myVar[]<- myValue . Personally, I had never seen such an assignment, but it had a highly useful effect-- it reshaped the assigned data myValue to the shape of myVar.

I would like to use this in my code. However the documentation for "<-" seems to be silent on it.

Is this a well established feature and one can rely on it to work in all cases?

Also, my guess is that it might be a side effect of a function call stack, i.e. calling <- and [ in sequence, but I could not figure out how. Can someone throw some light on that?

Here's an example--

# A dataframe
df1<- data.frame(a=1:4, b=11:14)

# simple assignment assigns to class of RHS
df1<- c(21:24,31:34)
df1 
#[1] 21 22 23 24 31 32 33 34
class(df1)
#[1] "integer"

#assignment with [] casts to class of LHS 
df1<- data.frame(a=1:4, b=11:14)
df1[]<- c(21:24,31:34)
df1

#    a  b
# 1 21 31
# 2 22 32
# 3 23 33
# 4 24 34


# recycling to preserve shape
df1[]<- c(101:102)
df1

#     a   b
# 1 101 101
# 2 102 102
# 3 101 101
# 4 102 102

class(df1)
#data.frame

# reshaping 

df1<- data.frame(a=1:4, b=11:14)
df1[] <- matrix(1:8, 2,4)
df1 #matrix reshaped 
class(df1)
#[1] "data.frame"

# flattening 
x<- 1:8
x[]<- matrix(1:8,4,2)
x
#[1] 1 2 3 4 5 6 7 8

回答1:

This is an intentional and documented feature. As joran mentioned, the documentation page "Extract" includes this in the "Atomic Vectors" section:

An empty index selects all values: this is most often used to replace all the entries but keep the attributes.

However, in the case of recursive objects (data.frames or lists, for example), the attributes are only kept for the subsetted object. Its parts don't get such protection.

Here's an example:

animals <- factor(c('cat', 'dog', 'fish'))
df_factor <- data.frame(x = animals)
rownames(df_factor) <- c('meow', 'bark', 'blub')
str(df_factor)
# 'data.frame': 3 obs. of  1 variable:
#   $ x: Factor w/ 3 levels "cat","dog","fish": 1 2 3

df_factor[] <- 'cat'
str(df_factor)
# 'data.frame': 3 obs. of  1 variable:
#   $ x: chr  "cat" "cat" "cat"
rownames(df_factor)
# [1] "meow" "bark" "blub"

df_factor kept its rownames attribute, but the x column is just the character vector used in the assignment instead of a factor. We can keep the class and levels of x by specifically replacing its values:

df_factor <- data.frame(x = animals)
df_factor$x[] <- 'cat'
str(df_factor)
# 'data.frame': 3 obs. of  1 variable:
#   $ x: Factor w/ 3 levels "cat","dog","fish": 1 1 1

So replacement with empty subsetting is very safe for vectors, matrices, and arrays, because their elements can't have their own attributes. But it requires some care when dealing with list-like objects.