Identify duplicate islands in a matrix and change their values












5















I have a matrix:



m <- matrix(c(
1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 2,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 2, 2),
ncol = 6, byrow = TRUE)

[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2 # <- island 3, value 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 2 2 # <- island 4, also value 2


In this matrix, there are four 'islands', i.e. non-zero values separated by zeros:



(1) an island composed of three 1's, (2) four 3's, (3) one 2, and (4) two 2's.



Thus, two islands are composed of the value 2. I want to identify such 'duplicate' islands and change the values of one of the 'islands' (either will do) to the next available number (4 in this case):



     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 4 4









share|improve this question

























  • Not clear (I see 3 'islands', -- why is the next value 4?, ...). Please explain it more and give an expected output as well. Also make sure your examples are reproducible

    – Sotos
    Nov 20 '18 at 10:47











  • There are 4 islands: one made up of 1's, one made up of 3's and 2 made up of 2's. 4 is a number that hasn't already been taken and is one more than the maximum number already used.

    – user3651829
    Nov 20 '18 at 10:49











  • As I said in the original question, change either of the islands labelled '2' to '4' but not both.

    – user3651829
    Nov 20 '18 at 10:52
















5















I have a matrix:



m <- matrix(c(
1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 2,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 2, 2),
ncol = 6, byrow = TRUE)

[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2 # <- island 3, value 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 2 2 # <- island 4, also value 2


In this matrix, there are four 'islands', i.e. non-zero values separated by zeros:



(1) an island composed of three 1's, (2) four 3's, (3) one 2, and (4) two 2's.



Thus, two islands are composed of the value 2. I want to identify such 'duplicate' islands and change the values of one of the 'islands' (either will do) to the next available number (4 in this case):



     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 4 4









share|improve this question

























  • Not clear (I see 3 'islands', -- why is the next value 4?, ...). Please explain it more and give an expected output as well. Also make sure your examples are reproducible

    – Sotos
    Nov 20 '18 at 10:47











  • There are 4 islands: one made up of 1's, one made up of 3's and 2 made up of 2's. 4 is a number that hasn't already been taken and is one more than the maximum number already used.

    – user3651829
    Nov 20 '18 at 10:49











  • As I said in the original question, change either of the islands labelled '2' to '4' but not both.

    – user3651829
    Nov 20 '18 at 10:52














5












5








5


1






I have a matrix:



m <- matrix(c(
1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 2,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 2, 2),
ncol = 6, byrow = TRUE)

[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2 # <- island 3, value 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 2 2 # <- island 4, also value 2


In this matrix, there are four 'islands', i.e. non-zero values separated by zeros:



(1) an island composed of three 1's, (2) four 3's, (3) one 2, and (4) two 2's.



Thus, two islands are composed of the value 2. I want to identify such 'duplicate' islands and change the values of one of the 'islands' (either will do) to the next available number (4 in this case):



     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 4 4









share|improve this question
















I have a matrix:



m <- matrix(c(
1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 2,
3, 0, 0, 0, 0, 0,
3, 0, 0, 0, 2, 2),
ncol = 6, byrow = TRUE)

[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2 # <- island 3, value 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 2 2 # <- island 4, also value 2


In this matrix, there are four 'islands', i.e. non-zero values separated by zeros:



(1) an island composed of three 1's, (2) four 3's, (3) one 2, and (4) two 2's.



Thus, two islands are composed of the value 2. I want to identify such 'duplicate' islands and change the values of one of the 'islands' (either will do) to the next available number (4 in this case):



     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 1 0 0 0
[2,] 0 0 0 0 0 0
[3,] 3 0 0 0 0 0
[4,] 3 0 0 0 0 2
[5,] 3 0 0 0 0 0
[6,] 3 0 0 0 4 4






r matrix






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 20 '18 at 20:37









Henrik

41.7k994109




41.7k994109










asked Nov 20 '18 at 10:41









user3651829user3651829

3261312




3261312













  • Not clear (I see 3 'islands', -- why is the next value 4?, ...). Please explain it more and give an expected output as well. Also make sure your examples are reproducible

    – Sotos
    Nov 20 '18 at 10:47











  • There are 4 islands: one made up of 1's, one made up of 3's and 2 made up of 2's. 4 is a number that hasn't already been taken and is one more than the maximum number already used.

    – user3651829
    Nov 20 '18 at 10:49











  • As I said in the original question, change either of the islands labelled '2' to '4' but not both.

    – user3651829
    Nov 20 '18 at 10:52



















  • Not clear (I see 3 'islands', -- why is the next value 4?, ...). Please explain it more and give an expected output as well. Also make sure your examples are reproducible

    – Sotos
    Nov 20 '18 at 10:47











  • There are 4 islands: one made up of 1's, one made up of 3's and 2 made up of 2's. 4 is a number that hasn't already been taken and is one more than the maximum number already used.

    – user3651829
    Nov 20 '18 at 10:49











  • As I said in the original question, change either of the islands labelled '2' to '4' but not both.

    – user3651829
    Nov 20 '18 at 10:52

















Not clear (I see 3 'islands', -- why is the next value 4?, ...). Please explain it more and give an expected output as well. Also make sure your examples are reproducible

– Sotos
Nov 20 '18 at 10:47





Not clear (I see 3 'islands', -- why is the next value 4?, ...). Please explain it more and give an expected output as well. Also make sure your examples are reproducible

– Sotos
Nov 20 '18 at 10:47













There are 4 islands: one made up of 1's, one made up of 3's and 2 made up of 2's. 4 is a number that hasn't already been taken and is one more than the maximum number already used.

– user3651829
Nov 20 '18 at 10:49





There are 4 islands: one made up of 1's, one made up of 3's and 2 made up of 2's. 4 is a number that hasn't already been taken and is one more than the maximum number already used.

– user3651829
Nov 20 '18 at 10:49













As I said in the original question, change either of the islands labelled '2' to '4' but not both.

– user3651829
Nov 20 '18 at 10:52





As I said in the original question, change either of the islands labelled '2' to '4' but not both.

– user3651829
Nov 20 '18 at 10:52












4 Answers
4






active

oldest

votes


















2














Fun question! Let's take a more involved case



(M <- matrix(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 
0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 2, 0, 2), 6, 6))
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 1 1 1 0 0 1
# [2,] 0 0 0 0 0 0
# [3,] 3 0 3 3 0 0
# [4,] 3 0 0 0 0 2
# [5,] 3 0 2 0 0 0
# [6,] 3 0 0 0 2 2


Here's a graph-based solution.



library(igraph)
# Indices of nonzero matrix elements
idx <- which(M != 0, arr.ind = TRUE)
# Adjacency matrix for matrix entries
# Two entries are adjacent if their column or row number differs by one
# Also, due to idx, an implicit condition is also that the two entries are the same
adj <- 1 * (as.matrix(dist(idx, method = "manhattan")) == 1)
# Creating loops as to take into account singleton islands
diag(adj) <- 1
# A corresponding graphs
g <- graph_from_adjacency_matrix(adj, mode = "undirected")
# Connected components of this graph
cmps <- clusters(g)
# Going over unique values of M
for(i in 1:max(M)) {
# Islands of value i
un <- unique(cmps$membership[M[idx] == i])
# More than one island?
if(length(un) > 1)
# If so, let's go over islands 2, 3, ...
for(cmp in un[-1])
# ... and replace corresponding matrix entries by max(M) + 1
M[idx[cmps$membership == cmp, , drop = FALSE]] <- max(M) + 1
}

M
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 1 1 1 0 0 4
# [2,] 0 0 0 0 0 0
# [3,] 3 0 7 7 0 0
# [4,] 3 0 0 0 0 6
# [5,] 3 0 2 0 0 0
# [6,] 3 0 0 0 5 5


Also note that using adj alone we could find all the islands if we could find its permutation leading to a block-diagonal matrix with the maximal number of blocks. Then each block would correspond to an island. However, I couldn't find an R implementation of a relevant procedure.






share|improve this answer


























  • I was trying to use a depth first search, this is perfect.

    – user3651829
    Nov 20 '18 at 13:22



















1














'Islands' of non-zero values can be identified by raster::clump*. Then use data.table convenience functions to identify which values should be updated.



library(raster)
library(data.table)

# get index of non-zero values. re-order to match the clump order
ix <- which(m != 0, arr.ind = TRUE)
ix <- ix[order(ix[ , "row"]), ]

# get clumps
cl <- clump(raster(m))
cl_ix <- cl@data@values

# put stuff in a data.table and order by x
d <- data.table(ix, x = m[ix], cl_ix = cl_ix[!is.na(cl_ix)])
setorder(d, x, cl_ix)

# for each x, create a counter of runs of clump index
d[ , g := rleid(cl_ix), by = x]

# for 'duplicated' runs...
# ...add to x based on runs of x and clump index runs
d[g > 1, x := max(d$x) + rleid(x, g)]

# update matrix
m2 <- m
m2[as.matrix(d[ , .(row, col)])] <- d$x

m
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 1 1 1 0 0 1
# [2,] 0 0 0 0 0 0
# [3,] 3 0 3 3 0 0
# [4,] 3 0 0 0 0 2
# [5,] 3 0 2 0 0 0
# [6,] 3 0 0 0 2 2

m2
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 1 1 1 0 0 4
# [2,] 0 0 0 0 0 0
# [3,] 3 0 7 7 0 0
# [4,] 3 0 0 0 0 2
# [5,] 3 0 5 0 0 0
# [6,] 3 0 0 0 6 6




*Note that the clump function requires that the igraph package is available.






share|improve this answer

































    0














    It was harder than I thought becase of the "not both" condition, I achieved the result with a while loop for now, we'll se if it can be improved:



    (basically we move by row and check if the island is found, if so we end our research)



    # some useful variables
    i=1 # row counter
    counter=0 # check if island is found
    max_m <- max(m) #finds the max value in the matrix, to fill

    while(counter == 0) {

    if (any(m[i, ] == 2)) { # check if we find the island in the row, otherwise skip
    row <- m[i, ]
    row[row == 2] <- max_m + 1 # here we change the value
    m[i, ] <- row
    counter <- counter + 1
    }

    i = i + 1 # we move up one row
    #cat("row number: ", i, "n") # sanity check to see if it was an infinite loop
    }
    m
    # [,1] [,2] [,3] [,4] [,5] [,6]
    # [1,] 1 1 1 0 0 0
    # [2,] 0 0 0 0 0 0
    # [3,] 3 0 0 0 0 0
    # [4,] 3 0 0 0 0 4
    # [5,] 3 0 0 0 0 0
    # [6,] 3 0 0 0 2 2


    This is far from perfect, because we move by rows, so if the first island is across a column we would change only the first value.



    Example of unexpected result:



    #      [,1] [,2] [,3] [,4] [,5] [,6]
    # [1,] 1 1 1 0 0 0
    # [2,] 0 0 0 0 0 0
    # [3,] 3 0 0 0 0 0
    # [4,] 3 0 0 0 0 4
    # [5,] 3 0 0 0 0 2 # problem here
    # [6,] 3 0 0 0 0 0


    Data used:



    m <- matrix(c(rep(1, 3),
    rep(0, 9),
    3,
    rep(0, 5),
    3,
    rep(0, 4),
    2,
    3,
    rep(0, 5),
    3,
    rep(0,3),
    rep(2, 2)),ncol=6,nrow=6, byrow = T)





    share|improve this answer

































      0














      This can easily be achieved with package TraMineR.



      islander <- function(mat) {
      require(TraMineR)
      rows.mat.seq <- seqdef(mat) # seeks all sequences in rows
      cols.mat.seq <- seqdef(t(mat)) # tranposed version
      rows <- seqpm(rows.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in rows
      cols <- seqpm(cols.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in columns
      if (length(cols) == 0) { # the row case
      mat[rows, which(mat[rows, ] == 2)] <- 4
      return(mat)
      } else { # the column case
      mat[which(mat[, cols] == 2), cols] <- 4
      return(mat)
      }
      }


      Yielding



      > islander(row.mat)
      ...
      [,1] [,2] [,3] [,4] [,5] [,6]
      [1,] 1 1 1 0 0 0
      [2,] 0 0 0 0 0 0
      [3,] 3 0 0 0 0 0
      [4,] 3 0 0 0 0 2
      [5,] 3 0 0 0 0 0
      [6,] 3 0 0 0 4 4

      > islander(col.mat)
      ...
      [,1] [,2] [,3] [,4] [,5] [,6]
      [1,] 1 1 1 0 0 0
      [2,] 0 0 0 0 0 0
      [3,] 3 0 0 0 0 0
      [4,] 3 0 0 0 0 0
      [5,] 3 0 0 0 0 4
      [6,] 3 0 0 2 0 4


      Note: If your island is longer, you need to adept the code, e.g. for length of island is 3 do seqpm(., 222). It is certainly possible to implement the consideration of all cases into the function.



      Data



      row.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2), .Dim = c(6L,
      6L))
      col.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2), .Dim = c(6L,
      6L))

      > row.mat
      [,1] [,2] [,3] [,4] [,5] [,6]
      [1,] 1 1 1 0 0 0
      [2,] 0 0 0 0 0 0
      [3,] 3 0 0 0 0 0
      [4,] 3 0 0 0 0 2
      [5,] 3 0 0 0 0 0
      [6,] 3 0 0 0 2 2
      > col.mat
      [,1] [,2] [,3] [,4] [,5] [,6]
      [1,] 1 1 1 0 0 0
      [2,] 0 0 0 0 0 0
      [3,] 3 0 0 0 0 0
      [4,] 3 0 0 0 0 0
      [5,] 3 0 0 0 0 2
      [6,] 3 0 0 2 0 2





      share|improve this answer

























        Your Answer






        StackExchange.ifUsing("editor", function () {
        StackExchange.using("externalEditor", function () {
        StackExchange.using("snippets", function () {
        StackExchange.snippets.init();
        });
        });
        }, "code-snippets");

        StackExchange.ready(function() {
        var channelOptions = {
        tags: "".split(" "),
        id: "1"
        };
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function() {
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled) {
        StackExchange.using("snippets", function() {
        createEditor();
        });
        }
        else {
        createEditor();
        }
        });

        function createEditor() {
        StackExchange.prepareEditor({
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: true,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        imageUploader: {
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        },
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        });


        }
        });














        draft saved

        draft discarded


















        StackExchange.ready(
        function () {
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53391219%2fidentify-duplicate-islands-in-a-matrix-and-change-their-values%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown

























        4 Answers
        4






        active

        oldest

        votes








        4 Answers
        4






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        2














        Fun question! Let's take a more involved case



        (M <- matrix(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 
        0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 2, 0, 2), 6, 6))
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 1
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 3 3 0 0
        # [4,] 3 0 0 0 0 2
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 2 2


        Here's a graph-based solution.



        library(igraph)
        # Indices of nonzero matrix elements
        idx <- which(M != 0, arr.ind = TRUE)
        # Adjacency matrix for matrix entries
        # Two entries are adjacent if their column or row number differs by one
        # Also, due to idx, an implicit condition is also that the two entries are the same
        adj <- 1 * (as.matrix(dist(idx, method = "manhattan")) == 1)
        # Creating loops as to take into account singleton islands
        diag(adj) <- 1
        # A corresponding graphs
        g <- graph_from_adjacency_matrix(adj, mode = "undirected")
        # Connected components of this graph
        cmps <- clusters(g)
        # Going over unique values of M
        for(i in 1:max(M)) {
        # Islands of value i
        un <- unique(cmps$membership[M[idx] == i])
        # More than one island?
        if(length(un) > 1)
        # If so, let's go over islands 2, 3, ...
        for(cmp in un[-1])
        # ... and replace corresponding matrix entries by max(M) + 1
        M[idx[cmps$membership == cmp, , drop = FALSE]] <- max(M) + 1
        }

        M
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 4
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 7 7 0 0
        # [4,] 3 0 0 0 0 6
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 5 5


        Also note that using adj alone we could find all the islands if we could find its permutation leading to a block-diagonal matrix with the maximal number of blocks. Then each block would correspond to an island. However, I couldn't find an R implementation of a relevant procedure.






        share|improve this answer


























        • I was trying to use a depth first search, this is perfect.

          – user3651829
          Nov 20 '18 at 13:22
















        2














        Fun question! Let's take a more involved case



        (M <- matrix(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 
        0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 2, 0, 2), 6, 6))
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 1
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 3 3 0 0
        # [4,] 3 0 0 0 0 2
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 2 2


        Here's a graph-based solution.



        library(igraph)
        # Indices of nonzero matrix elements
        idx <- which(M != 0, arr.ind = TRUE)
        # Adjacency matrix for matrix entries
        # Two entries are adjacent if their column or row number differs by one
        # Also, due to idx, an implicit condition is also that the two entries are the same
        adj <- 1 * (as.matrix(dist(idx, method = "manhattan")) == 1)
        # Creating loops as to take into account singleton islands
        diag(adj) <- 1
        # A corresponding graphs
        g <- graph_from_adjacency_matrix(adj, mode = "undirected")
        # Connected components of this graph
        cmps <- clusters(g)
        # Going over unique values of M
        for(i in 1:max(M)) {
        # Islands of value i
        un <- unique(cmps$membership[M[idx] == i])
        # More than one island?
        if(length(un) > 1)
        # If so, let's go over islands 2, 3, ...
        for(cmp in un[-1])
        # ... and replace corresponding matrix entries by max(M) + 1
        M[idx[cmps$membership == cmp, , drop = FALSE]] <- max(M) + 1
        }

        M
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 4
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 7 7 0 0
        # [4,] 3 0 0 0 0 6
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 5 5


        Also note that using adj alone we could find all the islands if we could find its permutation leading to a block-diagonal matrix with the maximal number of blocks. Then each block would correspond to an island. However, I couldn't find an R implementation of a relevant procedure.






        share|improve this answer


























        • I was trying to use a depth first search, this is perfect.

          – user3651829
          Nov 20 '18 at 13:22














        2












        2








        2







        Fun question! Let's take a more involved case



        (M <- matrix(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 
        0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 2, 0, 2), 6, 6))
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 1
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 3 3 0 0
        # [4,] 3 0 0 0 0 2
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 2 2


        Here's a graph-based solution.



        library(igraph)
        # Indices of nonzero matrix elements
        idx <- which(M != 0, arr.ind = TRUE)
        # Adjacency matrix for matrix entries
        # Two entries are adjacent if their column or row number differs by one
        # Also, due to idx, an implicit condition is also that the two entries are the same
        adj <- 1 * (as.matrix(dist(idx, method = "manhattan")) == 1)
        # Creating loops as to take into account singleton islands
        diag(adj) <- 1
        # A corresponding graphs
        g <- graph_from_adjacency_matrix(adj, mode = "undirected")
        # Connected components of this graph
        cmps <- clusters(g)
        # Going over unique values of M
        for(i in 1:max(M)) {
        # Islands of value i
        un <- unique(cmps$membership[M[idx] == i])
        # More than one island?
        if(length(un) > 1)
        # If so, let's go over islands 2, 3, ...
        for(cmp in un[-1])
        # ... and replace corresponding matrix entries by max(M) + 1
        M[idx[cmps$membership == cmp, , drop = FALSE]] <- max(M) + 1
        }

        M
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 4
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 7 7 0 0
        # [4,] 3 0 0 0 0 6
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 5 5


        Also note that using adj alone we could find all the islands if we could find its permutation leading to a block-diagonal matrix with the maximal number of blocks. Then each block would correspond to an island. However, I couldn't find an R implementation of a relevant procedure.






        share|improve this answer















        Fun question! Let's take a more involved case



        (M <- matrix(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 
        0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 2, 0, 2), 6, 6))
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 1
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 3 3 0 0
        # [4,] 3 0 0 0 0 2
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 2 2


        Here's a graph-based solution.



        library(igraph)
        # Indices of nonzero matrix elements
        idx <- which(M != 0, arr.ind = TRUE)
        # Adjacency matrix for matrix entries
        # Two entries are adjacent if their column or row number differs by one
        # Also, due to idx, an implicit condition is also that the two entries are the same
        adj <- 1 * (as.matrix(dist(idx, method = "manhattan")) == 1)
        # Creating loops as to take into account singleton islands
        diag(adj) <- 1
        # A corresponding graphs
        g <- graph_from_adjacency_matrix(adj, mode = "undirected")
        # Connected components of this graph
        cmps <- clusters(g)
        # Going over unique values of M
        for(i in 1:max(M)) {
        # Islands of value i
        un <- unique(cmps$membership[M[idx] == i])
        # More than one island?
        if(length(un) > 1)
        # If so, let's go over islands 2, 3, ...
        for(cmp in un[-1])
        # ... and replace corresponding matrix entries by max(M) + 1
        M[idx[cmps$membership == cmp, , drop = FALSE]] <- max(M) + 1
        }

        M
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 4
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 7 7 0 0
        # [4,] 3 0 0 0 0 6
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 5 5


        Also note that using adj alone we could find all the islands if we could find its permutation leading to a block-diagonal matrix with the maximal number of blocks. Then each block would correspond to an island. However, I couldn't find an R implementation of a relevant procedure.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 20 '18 at 14:51

























        answered Nov 20 '18 at 12:18









        Julius VainoraJulius Vainora

        37.6k76584




        37.6k76584













        • I was trying to use a depth first search, this is perfect.

          – user3651829
          Nov 20 '18 at 13:22



















        • I was trying to use a depth first search, this is perfect.

          – user3651829
          Nov 20 '18 at 13:22

















        I was trying to use a depth first search, this is perfect.

        – user3651829
        Nov 20 '18 at 13:22





        I was trying to use a depth first search, this is perfect.

        – user3651829
        Nov 20 '18 at 13:22













        1














        'Islands' of non-zero values can be identified by raster::clump*. Then use data.table convenience functions to identify which values should be updated.



        library(raster)
        library(data.table)

        # get index of non-zero values. re-order to match the clump order
        ix <- which(m != 0, arr.ind = TRUE)
        ix <- ix[order(ix[ , "row"]), ]

        # get clumps
        cl <- clump(raster(m))
        cl_ix <- cl@data@values

        # put stuff in a data.table and order by x
        d <- data.table(ix, x = m[ix], cl_ix = cl_ix[!is.na(cl_ix)])
        setorder(d, x, cl_ix)

        # for each x, create a counter of runs of clump index
        d[ , g := rleid(cl_ix), by = x]

        # for 'duplicated' runs...
        # ...add to x based on runs of x and clump index runs
        d[g > 1, x := max(d$x) + rleid(x, g)]

        # update matrix
        m2 <- m
        m2[as.matrix(d[ , .(row, col)])] <- d$x

        m
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 1
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 3 3 0 0
        # [4,] 3 0 0 0 0 2
        # [5,] 3 0 2 0 0 0
        # [6,] 3 0 0 0 2 2

        m2
        # [,1] [,2] [,3] [,4] [,5] [,6]
        # [1,] 1 1 1 0 0 4
        # [2,] 0 0 0 0 0 0
        # [3,] 3 0 7 7 0 0
        # [4,] 3 0 0 0 0 2
        # [5,] 3 0 5 0 0 0
        # [6,] 3 0 0 0 6 6




        *Note that the clump function requires that the igraph package is available.






        share|improve this answer






























          1














          'Islands' of non-zero values can be identified by raster::clump*. Then use data.table convenience functions to identify which values should be updated.



          library(raster)
          library(data.table)

          # get index of non-zero values. re-order to match the clump order
          ix <- which(m != 0, arr.ind = TRUE)
          ix <- ix[order(ix[ , "row"]), ]

          # get clumps
          cl <- clump(raster(m))
          cl_ix <- cl@data@values

          # put stuff in a data.table and order by x
          d <- data.table(ix, x = m[ix], cl_ix = cl_ix[!is.na(cl_ix)])
          setorder(d, x, cl_ix)

          # for each x, create a counter of runs of clump index
          d[ , g := rleid(cl_ix), by = x]

          # for 'duplicated' runs...
          # ...add to x based on runs of x and clump index runs
          d[g > 1, x := max(d$x) + rleid(x, g)]

          # update matrix
          m2 <- m
          m2[as.matrix(d[ , .(row, col)])] <- d$x

          m
          # [,1] [,2] [,3] [,4] [,5] [,6]
          # [1,] 1 1 1 0 0 1
          # [2,] 0 0 0 0 0 0
          # [3,] 3 0 3 3 0 0
          # [4,] 3 0 0 0 0 2
          # [5,] 3 0 2 0 0 0
          # [6,] 3 0 0 0 2 2

          m2
          # [,1] [,2] [,3] [,4] [,5] [,6]
          # [1,] 1 1 1 0 0 4
          # [2,] 0 0 0 0 0 0
          # [3,] 3 0 7 7 0 0
          # [4,] 3 0 0 0 0 2
          # [5,] 3 0 5 0 0 0
          # [6,] 3 0 0 0 6 6




          *Note that the clump function requires that the igraph package is available.






          share|improve this answer




























            1












            1








            1







            'Islands' of non-zero values can be identified by raster::clump*. Then use data.table convenience functions to identify which values should be updated.



            library(raster)
            library(data.table)

            # get index of non-zero values. re-order to match the clump order
            ix <- which(m != 0, arr.ind = TRUE)
            ix <- ix[order(ix[ , "row"]), ]

            # get clumps
            cl <- clump(raster(m))
            cl_ix <- cl@data@values

            # put stuff in a data.table and order by x
            d <- data.table(ix, x = m[ix], cl_ix = cl_ix[!is.na(cl_ix)])
            setorder(d, x, cl_ix)

            # for each x, create a counter of runs of clump index
            d[ , g := rleid(cl_ix), by = x]

            # for 'duplicated' runs...
            # ...add to x based on runs of x and clump index runs
            d[g > 1, x := max(d$x) + rleid(x, g)]

            # update matrix
            m2 <- m
            m2[as.matrix(d[ , .(row, col)])] <- d$x

            m
            # [,1] [,2] [,3] [,4] [,5] [,6]
            # [1,] 1 1 1 0 0 1
            # [2,] 0 0 0 0 0 0
            # [3,] 3 0 3 3 0 0
            # [4,] 3 0 0 0 0 2
            # [5,] 3 0 2 0 0 0
            # [6,] 3 0 0 0 2 2

            m2
            # [,1] [,2] [,3] [,4] [,5] [,6]
            # [1,] 1 1 1 0 0 4
            # [2,] 0 0 0 0 0 0
            # [3,] 3 0 7 7 0 0
            # [4,] 3 0 0 0 0 2
            # [5,] 3 0 5 0 0 0
            # [6,] 3 0 0 0 6 6




            *Note that the clump function requires that the igraph package is available.






            share|improve this answer















            'Islands' of non-zero values can be identified by raster::clump*. Then use data.table convenience functions to identify which values should be updated.



            library(raster)
            library(data.table)

            # get index of non-zero values. re-order to match the clump order
            ix <- which(m != 0, arr.ind = TRUE)
            ix <- ix[order(ix[ , "row"]), ]

            # get clumps
            cl <- clump(raster(m))
            cl_ix <- cl@data@values

            # put stuff in a data.table and order by x
            d <- data.table(ix, x = m[ix], cl_ix = cl_ix[!is.na(cl_ix)])
            setorder(d, x, cl_ix)

            # for each x, create a counter of runs of clump index
            d[ , g := rleid(cl_ix), by = x]

            # for 'duplicated' runs...
            # ...add to x based on runs of x and clump index runs
            d[g > 1, x := max(d$x) + rleid(x, g)]

            # update matrix
            m2 <- m
            m2[as.matrix(d[ , .(row, col)])] <- d$x

            m
            # [,1] [,2] [,3] [,4] [,5] [,6]
            # [1,] 1 1 1 0 0 1
            # [2,] 0 0 0 0 0 0
            # [3,] 3 0 3 3 0 0
            # [4,] 3 0 0 0 0 2
            # [5,] 3 0 2 0 0 0
            # [6,] 3 0 0 0 2 2

            m2
            # [,1] [,2] [,3] [,4] [,5] [,6]
            # [1,] 1 1 1 0 0 4
            # [2,] 0 0 0 0 0 0
            # [3,] 3 0 7 7 0 0
            # [4,] 3 0 0 0 0 2
            # [5,] 3 0 5 0 0 0
            # [6,] 3 0 0 0 6 6




            *Note that the clump function requires that the igraph package is available.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 20 '18 at 19:47

























            answered Nov 20 '18 at 12:22









            HenrikHenrik

            41.7k994109




            41.7k994109























                0














                It was harder than I thought becase of the "not both" condition, I achieved the result with a while loop for now, we'll se if it can be improved:



                (basically we move by row and check if the island is found, if so we end our research)



                # some useful variables
                i=1 # row counter
                counter=0 # check if island is found
                max_m <- max(m) #finds the max value in the matrix, to fill

                while(counter == 0) {

                if (any(m[i, ] == 2)) { # check if we find the island in the row, otherwise skip
                row <- m[i, ]
                row[row == 2] <- max_m + 1 # here we change the value
                m[i, ] <- row
                counter <- counter + 1
                }

                i = i + 1 # we move up one row
                #cat("row number: ", i, "n") # sanity check to see if it was an infinite loop
                }
                m
                # [,1] [,2] [,3] [,4] [,5] [,6]
                # [1,] 1 1 1 0 0 0
                # [2,] 0 0 0 0 0 0
                # [3,] 3 0 0 0 0 0
                # [4,] 3 0 0 0 0 4
                # [5,] 3 0 0 0 0 0
                # [6,] 3 0 0 0 2 2


                This is far from perfect, because we move by rows, so if the first island is across a column we would change only the first value.



                Example of unexpected result:



                #      [,1] [,2] [,3] [,4] [,5] [,6]
                # [1,] 1 1 1 0 0 0
                # [2,] 0 0 0 0 0 0
                # [3,] 3 0 0 0 0 0
                # [4,] 3 0 0 0 0 4
                # [5,] 3 0 0 0 0 2 # problem here
                # [6,] 3 0 0 0 0 0


                Data used:



                m <- matrix(c(rep(1, 3),
                rep(0, 9),
                3,
                rep(0, 5),
                3,
                rep(0, 4),
                2,
                3,
                rep(0, 5),
                3,
                rep(0,3),
                rep(2, 2)),ncol=6,nrow=6, byrow = T)





                share|improve this answer






























                  0














                  It was harder than I thought becase of the "not both" condition, I achieved the result with a while loop for now, we'll se if it can be improved:



                  (basically we move by row and check if the island is found, if so we end our research)



                  # some useful variables
                  i=1 # row counter
                  counter=0 # check if island is found
                  max_m <- max(m) #finds the max value in the matrix, to fill

                  while(counter == 0) {

                  if (any(m[i, ] == 2)) { # check if we find the island in the row, otherwise skip
                  row <- m[i, ]
                  row[row == 2] <- max_m + 1 # here we change the value
                  m[i, ] <- row
                  counter <- counter + 1
                  }

                  i = i + 1 # we move up one row
                  #cat("row number: ", i, "n") # sanity check to see if it was an infinite loop
                  }
                  m
                  # [,1] [,2] [,3] [,4] [,5] [,6]
                  # [1,] 1 1 1 0 0 0
                  # [2,] 0 0 0 0 0 0
                  # [3,] 3 0 0 0 0 0
                  # [4,] 3 0 0 0 0 4
                  # [5,] 3 0 0 0 0 0
                  # [6,] 3 0 0 0 2 2


                  This is far from perfect, because we move by rows, so if the first island is across a column we would change only the first value.



                  Example of unexpected result:



                  #      [,1] [,2] [,3] [,4] [,5] [,6]
                  # [1,] 1 1 1 0 0 0
                  # [2,] 0 0 0 0 0 0
                  # [3,] 3 0 0 0 0 0
                  # [4,] 3 0 0 0 0 4
                  # [5,] 3 0 0 0 0 2 # problem here
                  # [6,] 3 0 0 0 0 0


                  Data used:



                  m <- matrix(c(rep(1, 3),
                  rep(0, 9),
                  3,
                  rep(0, 5),
                  3,
                  rep(0, 4),
                  2,
                  3,
                  rep(0, 5),
                  3,
                  rep(0,3),
                  rep(2, 2)),ncol=6,nrow=6, byrow = T)





                  share|improve this answer




























                    0












                    0








                    0







                    It was harder than I thought becase of the "not both" condition, I achieved the result with a while loop for now, we'll se if it can be improved:



                    (basically we move by row and check if the island is found, if so we end our research)



                    # some useful variables
                    i=1 # row counter
                    counter=0 # check if island is found
                    max_m <- max(m) #finds the max value in the matrix, to fill

                    while(counter == 0) {

                    if (any(m[i, ] == 2)) { # check if we find the island in the row, otherwise skip
                    row <- m[i, ]
                    row[row == 2] <- max_m + 1 # here we change the value
                    m[i, ] <- row
                    counter <- counter + 1
                    }

                    i = i + 1 # we move up one row
                    #cat("row number: ", i, "n") # sanity check to see if it was an infinite loop
                    }
                    m
                    # [,1] [,2] [,3] [,4] [,5] [,6]
                    # [1,] 1 1 1 0 0 0
                    # [2,] 0 0 0 0 0 0
                    # [3,] 3 0 0 0 0 0
                    # [4,] 3 0 0 0 0 4
                    # [5,] 3 0 0 0 0 0
                    # [6,] 3 0 0 0 2 2


                    This is far from perfect, because we move by rows, so if the first island is across a column we would change only the first value.



                    Example of unexpected result:



                    #      [,1] [,2] [,3] [,4] [,5] [,6]
                    # [1,] 1 1 1 0 0 0
                    # [2,] 0 0 0 0 0 0
                    # [3,] 3 0 0 0 0 0
                    # [4,] 3 0 0 0 0 4
                    # [5,] 3 0 0 0 0 2 # problem here
                    # [6,] 3 0 0 0 0 0


                    Data used:



                    m <- matrix(c(rep(1, 3),
                    rep(0, 9),
                    3,
                    rep(0, 5),
                    3,
                    rep(0, 4),
                    2,
                    3,
                    rep(0, 5),
                    3,
                    rep(0,3),
                    rep(2, 2)),ncol=6,nrow=6, byrow = T)





                    share|improve this answer















                    It was harder than I thought becase of the "not both" condition, I achieved the result with a while loop for now, we'll se if it can be improved:



                    (basically we move by row and check if the island is found, if so we end our research)



                    # some useful variables
                    i=1 # row counter
                    counter=0 # check if island is found
                    max_m <- max(m) #finds the max value in the matrix, to fill

                    while(counter == 0) {

                    if (any(m[i, ] == 2)) { # check if we find the island in the row, otherwise skip
                    row <- m[i, ]
                    row[row == 2] <- max_m + 1 # here we change the value
                    m[i, ] <- row
                    counter <- counter + 1
                    }

                    i = i + 1 # we move up one row
                    #cat("row number: ", i, "n") # sanity check to see if it was an infinite loop
                    }
                    m
                    # [,1] [,2] [,3] [,4] [,5] [,6]
                    # [1,] 1 1 1 0 0 0
                    # [2,] 0 0 0 0 0 0
                    # [3,] 3 0 0 0 0 0
                    # [4,] 3 0 0 0 0 4
                    # [5,] 3 0 0 0 0 0
                    # [6,] 3 0 0 0 2 2


                    This is far from perfect, because we move by rows, so if the first island is across a column we would change only the first value.



                    Example of unexpected result:



                    #      [,1] [,2] [,3] [,4] [,5] [,6]
                    # [1,] 1 1 1 0 0 0
                    # [2,] 0 0 0 0 0 0
                    # [3,] 3 0 0 0 0 0
                    # [4,] 3 0 0 0 0 4
                    # [5,] 3 0 0 0 0 2 # problem here
                    # [6,] 3 0 0 0 0 0


                    Data used:



                    m <- matrix(c(rep(1, 3),
                    rep(0, 9),
                    3,
                    rep(0, 5),
                    3,
                    rep(0, 4),
                    2,
                    3,
                    rep(0, 5),
                    3,
                    rep(0,3),
                    rep(2, 2)),ncol=6,nrow=6, byrow = T)






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Nov 20 '18 at 11:20

























                    answered Nov 20 '18 at 11:15









                    RLaveRLave

                    4,60211024




                    4,60211024























                        0














                        This can easily be achieved with package TraMineR.



                        islander <- function(mat) {
                        require(TraMineR)
                        rows.mat.seq <- seqdef(mat) # seeks all sequences in rows
                        cols.mat.seq <- seqdef(t(mat)) # tranposed version
                        rows <- seqpm(rows.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in rows
                        cols <- seqpm(cols.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in columns
                        if (length(cols) == 0) { # the row case
                        mat[rows, which(mat[rows, ] == 2)] <- 4
                        return(mat)
                        } else { # the column case
                        mat[which(mat[, cols] == 2), cols] <- 4
                        return(mat)
                        }
                        }


                        Yielding



                        > islander(row.mat)
                        ...
                        [,1] [,2] [,3] [,4] [,5] [,6]
                        [1,] 1 1 1 0 0 0
                        [2,] 0 0 0 0 0 0
                        [3,] 3 0 0 0 0 0
                        [4,] 3 0 0 0 0 2
                        [5,] 3 0 0 0 0 0
                        [6,] 3 0 0 0 4 4

                        > islander(col.mat)
                        ...
                        [,1] [,2] [,3] [,4] [,5] [,6]
                        [1,] 1 1 1 0 0 0
                        [2,] 0 0 0 0 0 0
                        [3,] 3 0 0 0 0 0
                        [4,] 3 0 0 0 0 0
                        [5,] 3 0 0 0 0 4
                        [6,] 3 0 0 2 0 4


                        Note: If your island is longer, you need to adept the code, e.g. for length of island is 3 do seqpm(., 222). It is certainly possible to implement the consideration of all cases into the function.



                        Data



                        row.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2), .Dim = c(6L,
                        6L))
                        col.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
                        0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2), .Dim = c(6L,
                        6L))

                        > row.mat
                        [,1] [,2] [,3] [,4] [,5] [,6]
                        [1,] 1 1 1 0 0 0
                        [2,] 0 0 0 0 0 0
                        [3,] 3 0 0 0 0 0
                        [4,] 3 0 0 0 0 2
                        [5,] 3 0 0 0 0 0
                        [6,] 3 0 0 0 2 2
                        > col.mat
                        [,1] [,2] [,3] [,4] [,5] [,6]
                        [1,] 1 1 1 0 0 0
                        [2,] 0 0 0 0 0 0
                        [3,] 3 0 0 0 0 0
                        [4,] 3 0 0 0 0 0
                        [5,] 3 0 0 0 0 2
                        [6,] 3 0 0 2 0 2





                        share|improve this answer






























                          0














                          This can easily be achieved with package TraMineR.



                          islander <- function(mat) {
                          require(TraMineR)
                          rows.mat.seq <- seqdef(mat) # seeks all sequences in rows
                          cols.mat.seq <- seqdef(t(mat)) # tranposed version
                          rows <- seqpm(rows.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in rows
                          cols <- seqpm(cols.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in columns
                          if (length(cols) == 0) { # the row case
                          mat[rows, which(mat[rows, ] == 2)] <- 4
                          return(mat)
                          } else { # the column case
                          mat[which(mat[, cols] == 2), cols] <- 4
                          return(mat)
                          }
                          }


                          Yielding



                          > islander(row.mat)
                          ...
                          [,1] [,2] [,3] [,4] [,5] [,6]
                          [1,] 1 1 1 0 0 0
                          [2,] 0 0 0 0 0 0
                          [3,] 3 0 0 0 0 0
                          [4,] 3 0 0 0 0 2
                          [5,] 3 0 0 0 0 0
                          [6,] 3 0 0 0 4 4

                          > islander(col.mat)
                          ...
                          [,1] [,2] [,3] [,4] [,5] [,6]
                          [1,] 1 1 1 0 0 0
                          [2,] 0 0 0 0 0 0
                          [3,] 3 0 0 0 0 0
                          [4,] 3 0 0 0 0 0
                          [5,] 3 0 0 0 0 4
                          [6,] 3 0 0 2 0 4


                          Note: If your island is longer, you need to adept the code, e.g. for length of island is 3 do seqpm(., 222). It is certainly possible to implement the consideration of all cases into the function.



                          Data



                          row.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2), .Dim = c(6L,
                          6L))
                          col.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
                          0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2), .Dim = c(6L,
                          6L))

                          > row.mat
                          [,1] [,2] [,3] [,4] [,5] [,6]
                          [1,] 1 1 1 0 0 0
                          [2,] 0 0 0 0 0 0
                          [3,] 3 0 0 0 0 0
                          [4,] 3 0 0 0 0 2
                          [5,] 3 0 0 0 0 0
                          [6,] 3 0 0 0 2 2
                          > col.mat
                          [,1] [,2] [,3] [,4] [,5] [,6]
                          [1,] 1 1 1 0 0 0
                          [2,] 0 0 0 0 0 0
                          [3,] 3 0 0 0 0 0
                          [4,] 3 0 0 0 0 0
                          [5,] 3 0 0 0 0 2
                          [6,] 3 0 0 2 0 2





                          share|improve this answer




























                            0












                            0








                            0







                            This can easily be achieved with package TraMineR.



                            islander <- function(mat) {
                            require(TraMineR)
                            rows.mat.seq <- seqdef(mat) # seeks all sequences in rows
                            cols.mat.seq <- seqdef(t(mat)) # tranposed version
                            rows <- seqpm(rows.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in rows
                            cols <- seqpm(cols.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in columns
                            if (length(cols) == 0) { # the row case
                            mat[rows, which(mat[rows, ] == 2)] <- 4
                            return(mat)
                            } else { # the column case
                            mat[which(mat[, cols] == 2), cols] <- 4
                            return(mat)
                            }
                            }


                            Yielding



                            > islander(row.mat)
                            ...
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 2
                            [5,] 3 0 0 0 0 0
                            [6,] 3 0 0 0 4 4

                            > islander(col.mat)
                            ...
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 0
                            [5,] 3 0 0 0 0 4
                            [6,] 3 0 0 2 0 4


                            Note: If your island is longer, you need to adept the code, e.g. for length of island is 3 do seqpm(., 222). It is certainly possible to implement the consideration of all cases into the function.



                            Data



                            row.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2), .Dim = c(6L,
                            6L))
                            col.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
                            0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2), .Dim = c(6L,
                            6L))

                            > row.mat
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 2
                            [5,] 3 0 0 0 0 0
                            [6,] 3 0 0 0 2 2
                            > col.mat
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 0
                            [5,] 3 0 0 0 0 2
                            [6,] 3 0 0 2 0 2





                            share|improve this answer















                            This can easily be achieved with package TraMineR.



                            islander <- function(mat) {
                            require(TraMineR)
                            rows.mat.seq <- seqdef(mat) # seeks all sequences in rows
                            cols.mat.seq <- seqdef(t(mat)) # tranposed version
                            rows <- seqpm(rows.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in rows
                            cols <- seqpm(cols.mat.seq, 22)$MIndex # seeks for sub sequence 2-2 in columns
                            if (length(cols) == 0) { # the row case
                            mat[rows, which(mat[rows, ] == 2)] <- 4
                            return(mat)
                            } else { # the column case
                            mat[which(mat[, cols] == 2), cols] <- 4
                            return(mat)
                            }
                            }


                            Yielding



                            > islander(row.mat)
                            ...
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 2
                            [5,] 3 0 0 0 0 0
                            [6,] 3 0 0 0 4 4

                            > islander(col.mat)
                            ...
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 0
                            [5,] 3 0 0 0 0 4
                            [6,] 3 0 0 2 0 4


                            Note: If your island is longer, you need to adept the code, e.g. for length of island is 3 do seqpm(., 222). It is certainly possible to implement the consideration of all cases into the function.



                            Data



                            row.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2), .Dim = c(6L,
                            6L))
                            col.mat <- structure(c(1, 0, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
                            0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2), .Dim = c(6L,
                            6L))

                            > row.mat
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 2
                            [5,] 3 0 0 0 0 0
                            [6,] 3 0 0 0 2 2
                            > col.mat
                            [,1] [,2] [,3] [,4] [,5] [,6]
                            [1,] 1 1 1 0 0 0
                            [2,] 0 0 0 0 0 0
                            [3,] 3 0 0 0 0 0
                            [4,] 3 0 0 0 0 0
                            [5,] 3 0 0 0 0 2
                            [6,] 3 0 0 2 0 2






                            share|improve this answer














                            share|improve this answer



                            share|improve this answer








                            edited Nov 20 '18 at 13:20

























                            answered Nov 20 '18 at 13:05









                            jay.sfjay.sf

                            5,21721639




                            5,21721639






























                                draft saved

                                draft discarded




















































                                Thanks for contributing an answer to Stack Overflow!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid



                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.


                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function () {
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53391219%2fidentify-duplicate-islands-in-a-matrix-and-change-their-values%23new-answer', 'question_page');
                                }
                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown







                                Popular posts from this blog

                                Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

                                ComboBox Display Member on multiple fields

                                Is it possible to collect Nectar points via Trainline?