Search code examples
rapplysapply

Using rep inside sapply to strech a vector according to another vector


I want to generate a data.frame of edges. Problems arise when many edges end on one node. Edges are defined in vectors from and to.

# Data
vertices <- data.frame(id = 1:3, label = c("a", "b", "c"), stringsAsFactors = FALSE)
to <- c("a", "b", "c")
from1 <- c("c", "a", "b")
from2 <- c("c", "a", "a,b,c")

What I tried:

# Attempt 1
create_edges_1 <- function(from, to) {
  to <- sapply(to, function(x){vertices$id[vertices$label == x]})
  from <- sapply(from, function(x){vertices$id[vertices$label == x]})
  data.frame(from = from, to = to, stringsAsFactors = FALSE)
}

This works for example create_edges_1(from1, to), the output is:

  from to
c    3  1
a    1  2
b    2  3

However for example from2 this attempt fails.

So I tried the following:

# Attempt 2
create_edges_2 <- function(from, to) {
  to <- sapply(unlist(sapply(strsplit(to, ","), function(x){vertices$id[vertices$label == x]})), function(x){rep(x, sapply(strsplit(from2, ","), length))})
  from <- unlist(sapply(strsplit(from2, ","), function(x){vertices$id[vertices$label == x]}))
  data.frame(from = from, to = to, stringsAsFactors = FALSE)
}

The idea was to "stretch" to for every node where more than one edge ends. However create_edges_2(from1, to) and create_edges_2(from2, to) both throw an error

Error in rep(x, sapply(strsplit(from2, ","), length)) : invalid 'times' argument

What am I doing wrong in my sapply statements?

The expected output for create_edges_2(from2, to) is:

  from to
     3  1
     1  2
     1  3
     2  3
     3  3

Solution

  • You could use joins or match for this

    f2 <- strsplit(from2, ',')
    
    df <- data.frame(from = unlist(f2)
                     , to = rep(to, lengths(f2))
                     , stringsAsFactors = FALSE)
    

    With match

    library(tidyverse)
    
    map_dfc(df, ~ with(vertices, id[match(.x, label)]))
    
    # # A tibble: 5 x 2
    #    from    to
    #   <int> <int>
    # 1     3     1
    # 2     1     2
    # 3     1     3
    # 4     2     3
    # 5     3     3
    

    With Joins

    library(dplyr)
    
    df %>% 
      inner_join(vertices, by = c(from = 'label')) %>% 
      inner_join(vertices, by = c(to = 'label')) %>% 
      select_at(vars(matches('.x|.y')))
    
    #   id.x id.y
    # 1    3    1
    # 2    1    2
    # 3    1    3
    # 4    2    3
    # 5    3    3