tests/testthat/test-named_triplet.R

test_that("to_triplet() works as expected", {
  # Create a sparse matrix, with only non-zero rows and cols
  m <- matrix(c(1, 2, 
                3, 4, 
                5, 6), 
              nrow = 3, ncol = 2, 
              dimnames = list(c("r5", "r9", "r7"), 
                              c("c4", "c3"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  r_indices <- data.frame(names = c("r5", "r9", "r7", "r100"),
                          indices = as.integer(c(5, 9, 7, 100))) 
  c_indices <- data.frame(names = c("c4", "c3", "c100"), 
                          indices = as.integer(c(4, 3, 100)))
  # Try with a single data frame
  indices <- dplyr::bind_rows(r_indices, c_indices)
  expected <- tibble::tribble(~i, ~j, ~value, 
                              9, 3, 5, 
                              7, 3, 6, 
                              5, 3, 4, 
                              9, 4, 2, 
                              7, 4, 3, 
                              5, 4, 1) |> 
    setrowtype("rows") |> setcoltype("cols")
  # This should error, because we need a list of 2 or more
  expect_error(to_triplet(m, indices), regexp = "All indices and names must be unique in to_triplet")
  
  # Try with 2 unnamed data frames
  indices2 <- list(r_indices, c_indices)
  expect_equal(to_triplet(m, indices2) |> 
                 dplyr::arrange(i, j),
               expected |> 
                 dplyr::arrange(i, j))
  
  # Try with the index data frames in the wrong order.
  indices3 <- list(c_indices, r_indices)
  expect_error(to_triplet(m, indices3))
  
  # Try with named data frames (and add a third to test the code)
  unused_rindices <- data.frame(rnames = c("r3u", "r1u", "r2u", "r0u"),
                                rindices = as.integer(c(5, 9, 7, 100))) 
  unused_cindices <- data.frame(cnames = c("c2u", "c1u", "c3u"), 
                                cindices = as.integer(c(4, 3, 100)))
  indices4 <- list(unusedr = unused_rindices, 
                   cols = c_indices, 
                   rows = r_indices, 
                   unusedc = unused_cindices)
  expect_equal(to_triplet(m, indices4) |> 
                 dplyr::arrange(i, j),
               expected |> 
                 dplyr::arrange(i, j))
  # Try again, with a non-sparse matrix
  m5 <- matrix(c(0, 0, 0, 0, 
                 0, 0, 0, 0, 
                 0, 0, 0, 0, 
                 0, 0, 0, 0, 
                 0, 0, 4, 1, 
                 0, 0, 0, 0, 
                 0, 0, 6, 3, 
                 0, 0, 0, 0, 
                 0, 0, 5, 2), 
               nrow = 9, ncol = 4, byrow = TRUE,
               dimnames = list(c("r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9"), 
                               c("c1", "c2", "c3", "c4"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  expect_equal(to_triplet(m5, indices4) |> 
                 dplyr::arrange(i, j),
               expected |> 
                 dplyr::arrange(i, j))
})


test_that("to_triplet() fails when index_map contains repeated information", {
  m <- matrix(c(1, 2, 
                3, 4, 
                5, 6), 
              nrow = 3, ncol = 2, 
              dimnames = list(c("r1", "r2", "r3"), 
                              c("c1", "c2"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  r_indices <- data.frame(names = c("r3", "r1", "r2", "r0", "r1"),
                          indices = as.integer(c(5, 9, 7, 100, 101))) 
  c_indices <- data.frame(names = c("c2", "c1", "c3", "c2"), 
                          indices = as.integer(c(4, 3, 100, 101)))
  expect_error(to_triplet(m, list(r_indices, c_indices)), 
               regexp = "All indices and names must be unique in to_triplet")
})


test_that("to_triplet() fails when the correct named index_map is not supplied", {
  m <- matrix(c(1, 2, 
                3, 4, 
                5, 6), 
              nrow = 3, ncol = 2, 
              dimnames = list(c("r1", "r2", "r3"), 
                              c("c1", "c2"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  r_indices <- data.frame(names = c("r3", "r1", "r2", "r0", "r1"),
                          indices = as.integer(c(5, 9, 7, 100, 101))) 
  c_indices <- data.frame(names = c("c2", "c1", "c3", "c2"), 
                          indices = as.integer(c(4, 3, 100, 101)))
  unused_rindices <- data.frame(rnames = c("r3u", "r1u", "r2u", "r0u"),
                                rindices = as.integer(c(5, 9, 7, 100))) 
  unused_cindices <- data.frame(cnames = c("c2u", "c1u", "c3u"), 
                                cindices = as.integer(c(4, 3, 100)))
  indices_missing_rows <- list(unusedr = unused_rindices, 
                               cols = c_indices, 
                               # Wrong name. It should be called "rows".
                               bogus = r_indices, 
                               unusedc = unused_cindices)
  expect_error(to_triplet(m, indices_missing_rows), regexp = "Suitable index map for row type 'rows' not found")
  indices_missing_cols <- list(unusedr = unused_rindices, 
                               # Wrong name. It should be called "cols".
                               bogus = c_indices, 
                               rows = r_indices, 
                               unusedc = unused_cindices)
  expect_error(to_triplet(m, indices_missing_cols), regexp = "Suitable index map for column type 'cols' not found")
})


test_that("to_triplet() works with lists", {
  m <- matrix(c(1, 2, 
                3, 4, 
                5, 6), 
              nrow = 3, ncol = 2, 
              dimnames = list(c("r1", "r2", "r3"), 
                              c("c1", "c2"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  m_list <- list(m, m, m)
  r_indices <- data.frame(names = c("r3", "r1", "r2", "r0"),
                          indices = as.integer(c(5, 9, 7, 100))) 
  c_indices <- data.frame(names = c("c2", "c1", "c3"), 
                          indices = as.integer(c(4, 3, 100)))
  expected <- tibble::tribble(~i, ~j, ~value, 
                              9, 3, 1, 
                              7, 3, 2, 
                              5, 3, 3, 
                              9, 4, 4, 
                              7, 4, 5, 
                              5, 4, 6) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  
  # Try with 2 unnamed data frames
  indices <- list(r_indices, c_indices)
  expect_equal(to_triplet(m_list, indices), 
               list(expected, expected, expected))
})

test_that("to_triplet() works with NULL", {
  r_indices <- data.frame(names = c("r3", "r1", "r2", "r0"),
                          indices = as.integer(c(5, 9, 7, 100))) 
  c_indices <- data.frame(names = c("c2", "c1", "c3"), 
                          indices = as.integer(c(4, 3, 100)))
  # Try with 2 unnamed data frames
  indices <- list(r_indices, c_indices)
  expect_null(to_triplet(NULL, indices))
  
  
  m <- matrix(c(1, 2, 
                3, 4, 
                5, 6), 
              nrow = 3, ncol = 2, 
              dimnames = list(c("r1", "r2", "r3"), 
                              c("c1", "c2"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  m_list <- list(m, NULL, m)
  expected <- tibble::tribble(~i, ~j, ~value, 
                              9, 3, 1, 
                              7, 3, 2, 
                              5, 3, 3, 
                              9, 4, 4, 
                              7, 4, 5, 
                              5, 4, 6) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  expect_equal(to_triplet(m_list, indices), 
               list(expected, NULL, expected))
})


test_that("to_triplet() error messages are informative", {
  # Create a sparse matrix, with only non-zero rows and cols
  m <- matrix(c(1, 2, 
                3, 4, 
                5, 6), 
              nrow = 3, ncol = 2, 
              dimnames = list(c("r5", "r9", "r7"), 
                              c("c4", "c3"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  # Create the row indices so that r5 and r9 are missing,
  # which will trigger NA in the triplet data frame
  r_indices <- data.frame(names = c("r7", "r100"),
                          indices = as.integer(c(7, 100))) 
  c_indices <- data.frame(names = c("c4", "c3", "c100"), 
                          indices = as.integer(c(4, 3, 100)))
  # Try with a single data frame
  indices <- list(r_indices, c_indices)
  expect_error(to_triplet(m, indices), regexp = "Unmatched row names in")

  # Create the row indices so that c3 and c4 are missing,
  # which will trigger NA in the triplet data frame
  r_indices2 <- data.frame(names = c("r5", "r9", "r7", "r100"),
                           indices = as.integer(c(5, 9, 7, 100))) 
  c_indices2 <- data.frame(names = c("c100"), 
                           indices = as.integer(c(100)))
  # Try with a single data frame
  indices2 <- list(r_indices2, c_indices2)
  expect_error(to_triplet(m, indices2), regexp = "Unmatched column names in")
})


test_that("to_triplet() retains zero matrix structure when asked", {
  m <- matrix(c(0, 0, 
                0, 0, 
                0, 0), 
              nrow = 3, ncol = 2, 
              dimnames = list(c("r1", "r2", "r3"), 
                              c("c1", "c2"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  r_indices <- data.frame(names = c("r3", "r1", "r2", "r0"),
                          indices = as.integer(c(5, 9, 7, 100))) 
  c_indices <- data.frame(names = c("c2", "c1", "c3"), 
                          indices = as.integer(c(4, 3, 100)))
  # Try without retaining zero matrix structure
  expect_equal(to_triplet(m, list(r_indices, c_indices)),
               # Expect a zero-row data frame with correct row and column types
               data.frame(i = as.integer(0), j = as.integer(0), value = 3.1415926) |> 
                 dplyr::filter(FALSE) |> 
                 tibble::as_tibble() |> 
                 matsbyname::setrowtype("rows") |> 
                 matsbyname::setcoltype("cols"))
  # Now retain zero matrix structure
  expect_equal(to_triplet(m, list(r_indices, c_indices), retain_zero_structure = TRUE),
               # Expect a zero-row data frame with correct row and column types
               data.frame(               # r1 r2 r3 r1 r2 r3
                                         # c1 c1 c1 c2 c2 c2
                          i = as.integer(c(9, 7, 5, 9, 7, 5)),
                          j = as.integer(c(3, 3, 3, 4, 4, 4)), 
                          value = 0) |> 
                 tibble::as_tibble() |> 
                 matsbyname::setrowtype("rows") |> 
                 matsbyname::setcoltype("cols"))
})


test_that("to_named_matrix() works as expected", {
  triplet <- data.frame(i = as.integer(c(9, 7, 5, 9, 7, 5)), 
                        j = as.integer(c(3, 3, 3, 4, 4, 4)), 
                        value = c(1, 2, 3, 4, 5, 6)) |> 
    setrowtype("rows") |> setcoltype("cols")

  r_indices <- data.frame(names = paste0("r", 1:101),
                          indices = 1:101)
  c_indices <- data.frame(names = paste0("c", 1:101),
                          indices = 1:101)
  indices <- list(r_indices, c_indices)
  expected <- matrix(c(1, 2,
                       3, 4,
                       5, 6),
                     nrow = 3, ncol = 2,
                     dimnames = list(c("r9", "r7", "r5"),
                                     c("c3", "c4"))) |>
    sort_rows_cols() |> 
    setrowtype("rows") |>
    setcoltype("cols")

  expect_equal(to_named_matrix(triplet, indices), expected)
  # Try with a Matrix object returned
  expect_true(matsbyname:::equal_matrix_or_Matrix(to_named_matrix(triplet, indices, matrix_class = "Matrix"),
                                                  expected))
  
  # Try with a list
  expect_equal(to_named_matrix(list(triplet, triplet), indices), list(expected, expected))
})


test_that("to_named_matrix() fails when only one index_map is supplied in a list", {
  triplet <- data.frame(i = as.integer(c(9, 7, 5, 9, 7, 5)), 
                        j = as.integer(c(3, 3, 3, 4, 4, 4)), 
                        value = c(1, 2, 3, 4, 5, 6)) |> 
    setrowtype("rows") |> setcoltype("cols")
  
  r_indices <- data.frame(names = paste0("r", 1:101),
                          indices = 1:101)
  indices <- list(r_indices)
  expect_error(to_named_matrix(triplet, indices), 
               regexp = "Incorrectly formatted index_map in matsbyname::get_row_col_index_maps")
})


test_that("to_named_matrx() works when a single data frame is supplied", {
  triplet <- data.frame(i = as.integer(c(9, 7, 5, 9, 7, 5)), 
                        j = as.integer(c(3, 3, 3, 4, 4, 4)), 
                        value =        c(1, 2, 3, 4, 5, 6)) |> 
    setrowtype("rows") |> setcoltype("cols")
  
  indices <- data.frame(names = c(paste0("r", 1:101), paste0("c", 1:101)),
                          indices = 1:202)
  expect_equal(to_named_matrix(triplet, indices), 
               matrix(c(3, 6, 
                        2, 5, 
                        1, 4), byrow = TRUE, nrow = 3, 
                      dimnames = list(c("r5", "r7", "r9"), c("r3", "r4"))) |> 
                 matsbyname::setrowtype("rows") |> matsbyname::setcoltype("cols"))
})


test_that("to_named_matrix() is reversible", {
  # Start with a triplet.
  triplet <- data.frame(i = as.integer(c(9, 7, 5, 9, 7, 5)), 
                        j = as.integer(c(3, 3, 3, 4, 4, 4)), 
                        value = c(1, 2, 3, 4, 5, 6)) |> 
    setrowtype("rows") |> setcoltype("cols")
  
  r_indices <- data.frame(names = paste0("r", 1:101),
                          indices = 1:101)
  c_indices <- data.frame(names = paste0("c", 1:101),
                          indices = 1:101)
  indices <- list(r_indices, c_indices)
  expected_named <- matrix(c(1, 2,
                             3, 4,
                             5, 6),
                           nrow = 3, ncol = 2,
                           dimnames = list(c("r9", "r7", "r5"),
                                           c("c3", "c4"))) |>
    sort_rows_cols() |> 
    setrowtype("rows") |>
    setcoltype("cols")
  # Convert to named
  named <- to_named_matrix(triplet, indices)
  expect_equal(named, expected_named)  
  
  # Now reverse the process
  triplet2 <- to_triplet(named, indices)
  expect_equal(triplet2 |> 
                 as.data.frame() |> 
                 dplyr::arrange(i, j), 
               triplet |> 
                 dplyr::arrange(i, j))
})  


test_that("to_triplet() is reversible", {
  m <- matrix(c(0, 0, 0, 0, 
                0, 0, 0, 0, 
                0, 0, 0, 0, 
                0, 0, 0, 0, 
                0, 0, 4, 1, 
                0, 0, 0, 0, 
                0, 0, 6, 3, 
                0, 0, 0, 0, 
                0, 0, 5, 2), 
              nrow = 9, ncol = 4, byrow = TRUE,
              dimnames = list(c("r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9"), 
                              c("c1", "c2", "c3", "c4"))) |> 
    setrowtype("rows") |> 
    setcoltype("cols")
  r_indices <- data.frame(names = paste0("r", 1:101),
                          indices = 1:101)
  c_indices <- data.frame(names = paste0("c", 1:101),
                          indices = 1:101)
  # Try with a single data frame
  indices <- list(r_indices, c_indices)
  
  triplet <- to_triplet(m, indices)
  expected <- tibble::tribble(~i, ~j, ~value, 
                              9, 3, 5, 
                              7, 3, 6, 
                              5, 3, 4, 
                              9, 4, 2, 
                              7, 4, 3, 
                              5, 4, 1) |> 
    setrowtype("rows") |> setcoltype("cols")
  expect_equal(triplet |> 
                 dplyr::arrange(i, j), 
               expected |> 
                 dplyr::arrange(i, j))
  m2 <- to_named_matrix(triplet, indices)
  expect_equal(m2, matrix(c(4, 1, 
                            6, 3, 
                            5, 2), nrow = 3, ncol = 2, byrow = TRUE, 
                          dimnames = list(c("r5", "r7", "r9"), 
                                          c("c3", "c4"))) |> 
                 setrowtype("rows") |> setcoltype("cols"))
})


test_that("to_named_matrix() works with names already replacing integers", {
  triplet <- tibble::tribble(~i, ~j, ~value, 
                             "r9", "c3", 5, 
                             "r7", "c3", 6, 
                             "r5", "c3", 4, 
                             "r9", "c4", 2, 
                             "r7", "c4", 3, 
                             "r5", "c4", 1) |> 
    setrowtype("rows") |> setcoltype("cols")
  res <- triplet |> 
    to_named_matrix()
  
  expect_equal(res, matrix(c(4, 1, 
                             6, 3, 
                             5, 2), byrow = TRUE, 
                           nrow = 3, ncol = 2, 
                           dimnames = list(c("r5", "r7", "r9"), 
                                           c("c3", "c4"))) |> 
                 setrowtype("rows") |> setcoltype("cols"))
})


test_that("to_named_matrix() fails when neither integer triplet nor character triplet", {
  triplet <- tibble::tribble(~i, ~j, ~value, 
                             9, "c3", 5, 
                             7, "c3", 6, 
                             5, "c3", 4, 
                             9, "c4", 2, 
                             7, "c4", 3, 
                             5, "c4", 1) |> 
    setrowtype("rows") |> setcoltype("cols")
  res <- triplet |> 
    to_named_matrix() |> 
    expect_error("`row_index_colname` and `col_index_colname` must both be all integer or all character in to_named_matrix")
})


test_that("create_triplet() correctly retains zero structure", {
  m <- matrix(c(0, 0, 0, 
                0, 0, 0), nrow = 2, dimnames = list(c("r1", "r2"), 
                                                    c("c1", "c2", "c3")))
  expect_equal(matsbyname:::create_triplet(m),
               data.frame(i = as.integer(0), j = as.integer(0), value = 3.1415926) |> 
                 dplyr::filter(FALSE) |> 
                 tibble::as_tibble())
  expect_equal(matsbyname:::create_triplet(m, retain_zero_structure = TRUE),
               tibble::tribble(~i, ~j, ~value, 
                               1, 1, 0, 
                               2, 1, 0, 
                               1, 2, 0, 
                               2, 2, 0, 
                               1, 3, 0, 
                               2, 3, 0))
})


test_that("to_named_matrx() returns zero rows and columns when requested", {
  zero_triplet <- data.frame(i = as.integer(c(1, 2, 1, 2, 1, 2)), 
                             j = as.integer(c(1, 1, 2, 2, 3, 3)), 
                             value = c(0, 0, 0, 0, 0, 0)) |> 
    setrowtype("rows") |> setcoltype("cols")
  rindices <- data.frame(i = as.integer(c(1, 2)), 
                         rownames = c("r10", "r20"))
  cindices <- data.frame(j = as.integer(c(1, 2, 3)), 
                         colnames = c("c1", "c2", "c3"))
  index_map <- list(rindices, cindices)
  expected <- matrix(c(0, 0, 0, 
                       0, 0, 0), byrow = TRUE, 
                     nrow = 2, ncol = 3,
                     dimnames = list(c("r10", "r20"), c("c1", "c2", "c3"))) |> 
    setrowtype("rows") |> setcoltype("cols")
  res <- to_named_matrix(zero_triplet, 
                         index_map = index_map, 
                         matrix_class = "Matrix")
  matsbyname:::expect_equal_matrix_or_Matrix(res, expected)
  
  # Try again with row and column names already characters
  zero_triplet_char <- data.frame(i = c("r10", "r20", "r10", "r20", "r10", "r20"), 
                                  j = c("c1", "c1", "c2", "c2", "c3", "c3"), 
                                  value = c(0, 0, 0, 0, 0, 0))
  res_char <- to_named_matrix(zero_triplet_char,
                              index_map = index_map, 
                              matrix_class = "Matrix") |> 
    setrowtype("rows") |> setcoltype("cols")
  matsbyname:::expect_equal_matrix_or_Matrix(res_char, expected)
})
MatthewHeun/matsbyname documentation built on Jan. 21, 2025, 9:51 p.m.