0
votes

I am trying to make this function run faster. Ideally, I would just run something like apply on the dataframe and have it spit out the results much more quickly than what I currently have. What the function does is it takes a data frame that looks like this

df
   Var1 Var2
    5    0
    9    0
    4    1
    6    1
    2    2
    4    2

then it goes through every row and checks what value from the other rows in the data frame are closest (in both Var1 and Var2) to that of the values of Var1 and Var2 in the row you are in. The output is then a list of which rows are closest to every other row. For example

myFunc(df)

[[1]]
[1] 3 4

[[2]]
integer(0)

[[3]]
[1] 1 6

[[4]]
[1] 1

[[5]]
integer(0)

[[6]]
[1] 3

So Row 1 is nearest in values to both rows 3 and 4, while row 2 has no other row near it. Here is myFunc

myFunc = function(t) {
x=matrix(); x2=list()
y = matrix(); y2 = list()
for (i in 1:nrow(t)){
    for (j in 1:nrow(t)){
        #this will check for other rows <= 1 from the row I am currently in
        if (abs(t[i,1] - t[j,1]) <= 1) {
            x[j] = j
        } else { x[j] = NA }
        if (abs(t[i,2] - t[j,2]) <= 1) {
            y[j] = j
        } else { y[j] = NA }
    }
    x2[[i]] = x
    y2[[i]] = y
}
for (i in 1:length(x2)){
    x2[[i]] = x2[[i]][!x2[[i]] == i]
    y2[[i]] = y2[[i]][!y2[[i]] == i]
}
x2 = lapply(x2, function(x) x[!is.na(x)])
y2 = lapply(y2, function(x) x[!is.na(x)])

#this intersects Var1 and Var2 to find factors that are close to both Var1 and Var2
z = list()
for (i in 1:length(x2)){
    z[[i]] = intersect(unlist(x2[[i]]), unlist(y2[[i]]))
}
return(z)}
2
From your code, it seems that something like apply(as.matrix(dist(DF, "maximum")) == 1L, 2, which) might be helpful? - alexis_laz

2 Answers

1
votes

Assuming you are working with two columns only and integers in both, you could calculate the actual distance between every point using dist with an euclidean distance. Then you can just filter those distances larger 2^0.5.

It will work if you are working with integers because if the distance in var1 or var2 is 2 or more you want to discard it, but in any of these cases it will always be >= 2.

If you are not working with integers, you can still use dist but working in each column individually and then filtering them.

I'm thinking something like:

d1 <- dist(df$Var1) # or d1 <- dist(Var1)
d2 <- dist(df$Var2) # or d2 <- dist(Var2)

Now you can filter which ones are less than 1

dF <- as.matrix(d1) <= 1 & as.matrix(d2) <= 1
diag(dF) <- NA

And to get the final result you can

colnames(dF) <- NULL
dF2 <- lapply(as.data.frame(dF), which)
names(dF2) <- 1:nrow(df)

# dF2
# $`1`
# [1] 3 4

# $`2`
# integer(0)

# $`3`
# [1] 1 6

# $`4`
# [1] 1

# $`5`
# integer(0)

# $`6`
# [1] 3
0
votes

Mmm... You have O(N2) algorithm. What about doing it like this:

  • Add index/ID column df$ID <- seq.int(nrow(df)) (O(N))
  • Sort df by Var1 using stable sort (O(N log(N)))
  • One linear pass over column doing differences, building logical vector (O(N))
  • Get indices from ID column (O(N))

same for Var2

then you could do your intersection of results