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]))])