rwinkel2000
rwinkel2000

Reputation: 136

How Can I Confirm that Two R objects have the same structure?

My test below doesn't work. Can anyone suggest a different approach?

===different contents, same structure, want "true" for comparison

> x<-c(1,2,3)
> y<-x
> identical(str(x),str(y))
 num [1:3] 1 2 3
 num [1:3] 1 2 3
[1] TRUE
> y[3]<-999
> identical(str(x),str(y))
 num [1:3] 1 2 3
 num [1:3] 1 2 999
[1] TRUE
> str(x)
 num [1:3] 1 2 3
> str(y)
 num [1:3] 1 2 999
> 

but this approach is wrong because this says x and z have the same structure!

> z<-list("a","b")
> identical(str(x),str(z))
 num [1:3] 1 2 3
List of 2
 $ : chr "a"
 $ : chr "b"
[1] TRUE

I'm trying this because I need a way to confirm that an R object I construct has exactly the same type as what is provided in R package examples.

Upvotes: 4

Views: 2274

Answers (5)

sedot
sedot

Reputation: 577

We have a new option in the quiver now that goes by the name of Waldo and solves your problem in a more robust way. However, I still think you may want to code your own comparison function so you know what is going on.

> library('waldo')
> x <- c(1,2,3)
> y <- x
> compare(x, y)
✓ No differences
> y[3] <- 999
> compare(x, y)
`old`: 1 2   3
`new`: 1 2 999

Upvotes: 0

Montgomery Clift
Montgomery Clift

Reputation: 444

I am answering the version of this problem posed in a comment, namely how to test whether two objects have the same type, class, names and other attributes, although possibly different data. For example, you may want to confirm that two matrices have the same dimensions, and the same row and column names, and are of the same type.

SameStruc <- function(x, y) identical(`is.na<-`(x), `is.na<-`(y))

We can test this on two matrices:

M1 <- matrix(1:30, 3, 10, dimnames=list(LETTERS[1:3], letters[1:10]))
M2 <- M1
M2[1:30] <- 129:100
SameStruc(M1, M2)  # TRUE
                   # M1 and M2 are both integer matrices, 3 x 10, with the same dimnames but different data
M3 <- M1
M3[3,5] <- 1 * M3[3,5]
all(M1 == M3)      # TRUE  (the numerical values of the contents of M1 and M3 are the same)
SameStruc(M1, M3)  # FALSE (M3 has type "double" while M1 has type "integer")

I haven't tested this function on lists or dataframes or any objects other than matrices. I think it should work, though, with the proviso that on a list it will just test the outermost list structure, it does not test the structure of each pair of list elements.

Also I assume this function is highly inefficient for large objects because it creates a copy of each object filled with NAs.

Upvotes: 0

sedot
sedot

Reputation: 577

This answer is quite late, but may help visitors facing the same issue.

I need a way to confirm that an R object I construct has exactly the same type as [...]

For this specific case, consider typeof(). However, this might not be what you want.

To check if the types of vectors in a data.frame match, have a look at sapply(df, typeof).

For a more general solution, I suggest building the check yourself, since for every use case, 'structure' can have a different meaning. Is it just about types? Would you like to differentiate between double andinteger? Or check for the names of a data.frame as well? Or its dimensions? What if everything is identical except for the levels of one factor?

Building it yourself has a big advantage: You know what is happening.

Other useful functions are dim(), ncol(), nrow(), names(), class(), attributes() and mode().

Upvotes: 0

Danny
Danny

Reputation: 101

Been a while since this question was asked, but I've been tackling a similar problem.

Came up with this function as a solution:

CompareStructure <-
  function(x, y) {
    # function to recursively compare a nested list of structure annotations
    # using pairwise comparisons
    TypeCompare <-
      function(xSTR, ySTR) {
        if (length(xSTR) == length(ySTR)) {
          all(mapply(
            xSTR,
            ySTR,
            FUN = function(xValue, yValue) {
              if (is.list(xValue) && is.list(yValue)) {
                all(TypeCompare(xValue, yValue))
              } else if (is.list(xValue) == is.list(yValue)) {
                identical(xValue, yValue)
              } else {
                FALSE
              }
            }
          ))
        } else {
          FALSE
        }
      }

    # if both inputs are lists
    if (is.list(x) && is.list(y)) {
      # use Rapply to recursively apply function down list
      xSTR <-
        rapply(
          x,
          f = function(values) {
            c(mode(values), length(values))
          },
          how = "list"
        )

      # use Rapply to recursively apply function down list
      ySTR <-
        rapply(
          y,
          f = function(values) {
            c(mode(values), length(values))
          },
          how = "list"
        )

      # call the compare function on both structure annotations
      return(TypeCompare(xSTR, ySTR))

    } else {
      # if inputs are not same class == automatic not same structure
      if (class(x) != class(y)) {
        FALSE
      } else {
        # get dimensions of the x input, if null get length
        xSTR <-
          if (is.null((dimsX <- dim(x)))) {
            length(x)
          } else {
            dimsX
          }

        # get dimensions of the y input, if null get length
        ySTR <-
          if (is.null((dimsY <- dim(y)))) {
            length(y)
          } else {
            dimsY
          }

        # call the compare function on both structure annotations
        return(TypeCompare(xSTR, ySTR))
      }
    }
  }

Compares the mode and length of elements in a nested list and classes and dimensions of no-list objects

Upvotes: 4

darwin
darwin

Reputation: 453

The function dput() is for structure.

x <- c(1, 2, 3)
y <- x
identical(dput(x), dput(y))
# c(1, 2, 3)
# c(1, 2, 3)
# [1] TRUE

z <- list("a", "b")
identical(dput(x), dput(z))
# c(1, 2, 3)
# list("a", "b")
# [1] FALSE

Upvotes: -2

Related Questions