/ / Цифрово сравнение по време на сливане в R - r, сливане, dataframe

Цифрово сравнение по време на сливане в R - r, сливане, dataframe

Dataframe d1:

x  y
4 10
6 20
7 30

Dataframe d2:

x   z
3 100
6 200
9 300

Как да се слея d1 и d2 от "x" където d1$x трябва да се съпоставят с точно съвпадение или с следващото по-голямо число в d2$x, Изходът трябва да изглежда по следния начин:

x   y    z
4  10  200 # (4 is matched against next higher value that is 6)
6  20  200 # (6 is matched against 6)
7  30  300 # (7 is matched against next higher value that is 9)

ако merge() не може да направи това, тогава има ли друг начин да направите това? За цикли са болезнено бавни.

Отговори:

2 за отговор № 1

Входни данни:

d1 <- data.frame(x=c(4,6,7), y=c(10,20,30))
d2 <- data.frame(x=c(3,6,9), z=c(100,200,300))

По принцип желаете да продължите d1 от нова колона. Така че нека го копираме.

d3 <- d1

След това предполагам това d2$x се сортира недесално и товаmax(d1$x) <= max(d2$x).

d3$z <- sapply(d1$x, function(x) d2$z[which(x <= d2$x)[1]])

Което гласи: за всеки x в d1$x, получавате най-малката стойност от d2$x който не е по - малък от. \ t x.

При тези предположения горепосоченото може да бъде написано и като (& би трябвало да е малко по-бързо):

d3$z <- sapply(d1$x, function(x) d2$z[which.max(x <= d2$x)])

В резултат получаваме:

d3
##   x  y   z
## 1 4 10 200
## 2 6 20 200
## 3 7 30 300

EDIT1: Вдъхновени от @MatthewLundberg cutбазиран на решение, тук се използва друг findInterval:

d3$z <- d2$z[findInterval(d1$x, d2$x+1)+1]

EDIT2: (Бенчмарк)

Примерни данни:

set.seed(123)
d1 <- data.frame(x=sort(sample(1:10000, 1000)), y=sort(sample(1:10000, 1000)))
d2 <- data.frame(x=sort(c(sample(1:10000, 999), 10000)), z=sort(sample(1:10000, 1000)))

Резултати:

microbenchmark::microbenchmark(
{d3 <- d1; d3$z <- d2$z[findInterval(d1$x, d2$x+1)+1] },
{d3 <- d1; d3$z <- sapply(d1$x, function(x) d2$z[which(x <= d2$x)[1]]) },
{d3 <- d1; d3$z <- sapply(d1$x, function(x) d2$z[which.max(x <= d2$x)]) },
{d1$x2 <- d2$x[as.numeric(cut(d1$x, c(-Inf, d2$x, Inf)))]; merge(d1, d2, by.x="x2", by.y="x")},
{d1a <- d1; setkey(setDT(d1a), x); d2a <- d2; setkey(setDT(d2a), x); d2a[d1a, roll=-Inf] }
)
## Unit: microseconds
##         expr       min            lq    median        uq       max neval
## findInterval   221.102      1357.558  1394.246  1429.767  17810.55   100
## which        66311.738     70619.518 85170.175 87674.762 220613.09   100
## which.max    69832.069     73225.755 83347.842 89549.326 118266.20   100
## cut           8095.411      8347.841  8498.486  8798.226  25531.58   100
## data.table    1668.998      1774.442  1878.028  1954.583  17974.10   100

4 за отговор № 2

Това е много лесно подвижните съединения с data.table:

require(data.table)   ## >= 1.9.2
setkey(setDT(d1), x)  ## convert to data.table, set key for the column to join on
setkey(setDT(d2), x)  ##  same as above

d2[d1, roll=-Inf]

#    x   z  y
# 1: 4 200 10
# 2: 6 200 20
# 3: 7 300 30

2 за отговор № 3

cut може да се използва за намиране на подходящите мачове в d2$x за стойностите в d1$x.

Изчисленията за намиране на съвпадения с cut е както следва:

as.numeric(cut(d1$x, c(-Inf, d2$x, Inf)))
## [1] 2 2 3

Това са стойностите:

d2$x[as.numeric(cut(d1$x, c(-Inf, d2$x, Inf)))]
[1] 6 6 9

Те могат да бъдат добавени към d1 и сливането се извършва:

d1$x2 <- d2$x[as.numeric(cut(d1$x, c(-Inf, d2$x, Inf)))]
merge(d1, d2, by.x="x2", by.y="x")
##   x2 x  y   z
## 1  6 4 10 200
## 2  6 6 20 200
## 3  9 7 30 300

След това добавената колона може да бъде отстранена, ако се желае.


1 за отговор № 4

Опитвам: sapply(d1$x,function(y) d2$z[d2$x > y][which.min(abs(y - d2$x[d2$x > y]))])