2016-09-24 5 views
2

У меня есть два data.frames разных размеров, и я ищу наиболее эффективный способ сопоставления строк из одного data.frame в другой и извлекать некоторую релевантную информацию.Строка, соответствующая data.frames разных размеров

Вот пример:

Две начальные data.frames, а и б, и желаемый результат:

a = data.frame(term = c("red", "salad", "rope", "ball", "tent", "plane", "gift", "meat"), 
       age = c(30, 24, 52, 44, 73, 44, 33, 12), 
       visits = c(5, 1, 3, 2, 8, 5, 19, 3)) 

b = data.frame(string = c("the red ball went over the fence", 
          "sorry to see that your tent fell down", 
          "the ball fell into the red salad", 
          "serious people eat peanuts on Sundays")) 

desired_result = data.frame(string = b$string, 
          num_matches = c(2, 1, 3, 0), 
          avg_age = c(37, 73, 32.66667, NA), 
          avg_visits = c(3.5, 8, 2.66667, NA)) 

Вот data.frames в более читаемом формате:

> a 
    term age visits 
1 red 30  5 
2 salad 24  1 
3 rope 52  3 
4 ball 44  2 
5 tent 73  8 
6 plane 44  5 
7 gift 33  19 
8 meat 12  3 

> b 
           string 
1  the red ball went over the fence 
2 sorry to see that your tent fell down 
3  the ball fell into the red salad 
4 serious people eat peanuts on Sundays 

> desired_result 
           string num_matches avg_age avg_visits 
1  the red ball went over the fence   2 37.00000 3.50000 
2 sorry to see that your tent fell down   1 73.00000 8.00000 
3  the ball fell into the red salad   3 32.66667 2.66667 
4 serious people eat peanuts on Sundays   0  NA   NA 
  • NUM_MATCHES это число "точки" в "строке"
  • avg_age является средний возраст «терминов», найденных в «строке»
  • avg_visits является среднее число посещений «условий», которая находится в «строке»

Любые идеи о том, как реализовать это в эффективный способ?

спасибо.

+0

avg_age и avg_visits также может быть равен нулю, если совпадений не найдено – dreww2

ответ

1

Использование data.table, обработать каждую строку с by = string. сохранить результаты сопоставления в списке, а затем агрегировать по результатам сопоставления.

Примечание: столбец matches - это список списков, в каждой ячейке есть список. Вам нужно обернуть результаты совпадения с помощью .(), который на самом деле другой list(), потому что data.table ожидает список для обычных столбцов.

library(data.table) 
library(stringr) 
a = data.table(term = c("red", "salad", "rope", "ball", "tent", "plane", "gift", "meat"), 
    age = c(30, 24, 52, 44, 73, 44, 33, 12), 
    visits = c(5, 1, 3, 2, 8, 5, 19, 3)) 
b = data.table(string = c("the red ball went over the fence", 
    "sorry to see that your tent fell down", 
    "the ball fell into the red salad", 
    "serious people eat peanuts on Sundays")) 

b[, matches := vector("list", .N)] 
b[, matches := .(list(str_detect(string, a[, term]))), by = string] 
b[, num_matches := sum(unlist(matches)), by = string] 
b[, avg_age := mean(a[unlist(matches), age]), by = string] 
b[, avg_visits := mean(a[unlist(matches), visits]), by = string] 
+0

Спасибо всем респондентам, .. ,Мне нравится это решение много b/c, как ясно каждый из шагов (что упрощает добавление дополнительных шагов/сложности). Он также использует str_detect, который использует всю строку и обобщает на термины, которые не являются ни одним словом. Примечание: добавлено «na.rm = T» в «sum (unlist (matches))» и «b [, matches: = NULL]», чтобы удалить столбец совпадений в конце – dreww2

0

Я бы построить desired_result одна часть за другой:

Поэтому вам нужно две функции, один подсчет и один, чтобы вычислить среднее значение.

Сначала для вхождений:

counter <- function(sentence, pattern) 
    { 
     count <-0 
     for (var in pattern) 
     { 
     if(grepl(pattern=var, sentence)) count <- count +1 
     } 
     return(count) 
    } 

Второй для двух средних, вы можете использовать функцию в обоих случаях:

average <- function(sentence, look_up) 
{ 
    pattern <- look_up[,1] 
    count <-0 
    summed <- 0 
    for (var in pattern) 
    { 
    if(grepl(pattern=var, sentence)) { 
     count <- count + 1 
     summed <- sum(look_up[look_up[,1]==var,2]) + summed 
    } 
    }   
    return(summed/count) 
} 

Это можно применить к данным таким образом:

Первый делать:

desire_result <- data.frame(string = b$string) 

Тогда, чтобы получить значения сделать:

desired_result$num_match<- sapply(b$string,counter,pattern=a$term) 
desired_result$avg_age<- sapply(b$string,average,look_up=a[,c(1,2)]) 
desired_result$avg_visit<- sapply(b$string,average,look_up=a[,c(1,3)]) 

Это дает вам прямо сейчас desired_result, как указано в вашем вопросе:

> desired_result 
           string num_match avg_age avg_visit 
1  the red ball went over the fence   2 37.00000 3.500000 
2 sorry to see that your tent fell down   1 73.00000 8.000000 
3  the ball fell into the red salad   3 32.66667 2.666667 
4 serious people eat peanuts on Sundays   0  NaN  NaN 
2

Вы можете попробовать это с базой R (не требуется пакет):

res <- t(apply(b, 1, function(x) { 
    l <- strsplit(x, " ") 
    r <- unlist(lapply(unlist(l), function(y) which(a$term==y))) 
    rbind(length(r), mean(a$age[r]), mean(a$visits[r])) 

})) 

res <- cbind(b, res) 
           # string 1  2  3 
# 1  the red ball went over the fence 2 37.00000 3.500000 
# 2 sorry to see that your tent fell down 1 73.00000 8.000000 
# 3  the ball fell into the red salad 3 32.66667 2.666667 
# 4 serious people eat peanuts on Sundays 0  NaN  NaN 
+0

strsplit может быть ограничен, если строка немногочисленна бит беспорядочный, как 'самолет''. – dracodoc

Смежные вопросы