для каждой строки в фрейме данных найдите, есть ли «близкая» строка в другом фрейме данных - PullRequest
0 голосов
/ 30 июня 2018

У меня есть следующий фрейм данных:

library(dplyr)
set.seed(42)
df <- data_frame(x = sample(seq(0, 1, 0.1), 5, replace = T), y = sample(seq(0, 1, 0.1), 5, replace = T), z= sample(seq(0, 1, 0.1), 5, replace = T) )

Для каждой строки в df я хотел бы выяснить, есть ли в df2 строка, близкая к ней («сосед») во всех столбцах, где «close» означает, что она не отличается более чем на 0,1 в каждом столбце.

Так, например, правильным соседом строки (1, 0.5, 0.5) будет (0.9, 0.6, 0.4). Второй набор данных

set.seed(42)
df2 <- data_frame(x = sample(seq(0, 1, 0.1), 10, replace = T), y = sample(seq(0, 1, 0.1), 10, replace = T), z= sample(seq(0, 1, 0.1), 10, replace = T) )

В этом случае нет «соседа», поэтому я должен получить «FALSE» для всех строк df.

Мои фактические фреймы данных намного больше этого (десятки столбцов и сотни тысяч строк, поэтому наименование должно быть очень общим, а не "x", "y" и "z".

У меня есть ощущение, что это можно сделать с помощью mutate и funs, например, я попробовал эту строку:

df <- df %>% mutate_all(funs(close = (. <= df2(, .)+0.1) & (. >= df2(, .)-0.1))

Но получил ошибку.

Есть идеи?

Ответы [ 3 ]

0 голосов
/ 30 июня 2018

Вот один из способов вычислить этот столбец без fuzzyjoin

library(tidyverse)

found <- 
  expand.grid(row.df  = seq(nrow(df)),
              row.df2 = seq(nrow(df2))) %>% 
      mutate(in.range = pmap_lgl(., ~ all(abs(df[.x,] - df2[.y,]) <= 0.1))) %>% 
      group_by(row.df) %>% 
      summarise_at('in.range', any) %>% 
      select(in.range)
0 голосов
/ 30 июня 2018

Подход машинного обучения к нахождению записи close в многомерном наборе данных - это евклидово расстояние.

Общий подход заключается в нормализации всех атрибутов. Сделайте диапазон для каждого столбца одинаковым: от нуля до единицы или от минуса до единицы. Это уравнивает эффект столбцов с большими и маленькими значениями. Когда используются более продвинутые подходы, можно отцентрировать скорректированные значения столбца на нуле Критерии испытаний масштабируются одинаково.

Следующим шагом является вычисление расстояния каждого наблюдения от его соседей. Если набор данных невелик или время вычислений дешевое, рассчитайте расстояние от каждого наблюдения до каждого другого. Евклидово расстояние от наблюдения 1 (строка 1) до наблюдения 2 (строка 2) равно sqrt ((X1 - X2) ^ 2 + sqrt ((Y1 - Y2) ^ 2 + ...). Выберите критерии и выберите.

В вашем случае критерий раздела проще. Два наблюдения близки, если ни один атрибут не превышает 0,1 от другого наблюдения. Я предполагаю, что df и df2 имеют одинаковое количество столбцов в одинаковом порядке. Я делаю предположение, что близкие наблюдения относительно редки. Мой подход говорит мне, как только мы обнаруживаем, что пара далека, прекратить расследование. Если у вас сотни тысяч строк, вы, вероятно, исчерпаете память, если попытаетесь рассчитать все комбинации одновременно.

~~~~~

У вас большая проблема. Если ваши наборы данных df и df2 состоят из ста тысяч строк и четырех дюжин столбцов, то машина должна выполнить сравнения 4,8e + 11. Система показателей в конце будет иметь результаты 1е + 10 (близкие или отдаленные). Я начал с некоторого поднабора, чтобы сделать сравнения со слезными результатами. R хотел матрицы одинакового размера. Клаге, который я придумал, был неудачным. Поэтому я регрессировал до дней Фортрана и делал это с помощью петель. Благодаря циклическому подходу вы можете решить проблему и закончить, не куря машину.

Из данных выборки я провел сравнения вручную, все 150 из них: nrow (df) * nrow (df2) * ncol (df). Не было никаких близких наблюдений в данных выборки по определению, которое вы дали.

Вот как я намеревался представить результаты перед переносом результатов в новый столбец в df.

    dfclose <- matrix(TRUE, nrow = nrow(df), ncol = nrow(df2))
    dfclose # Have a look

Эта матрица описывает расстояние от наблюдения в df (строки в dfclose) до наблюдения в df2 (столбцы в dfclose). Если закрыть, запись TRUE.

Вот хранилище результата измерения расстояния:

    dfdist <- matrix(0, nrow = nrow(df), ncol = nrow(df2))
    dfdist # have a look; it's the same format, but with numbers

Начнем с предположения, что все наблюдения в df a близки к df2. Общее расстояние равно нулю. К этому мы добавим Манхэттенское Расстояние. Когда общее расстояние Манхэттена больше, чем 0,1, они больше не находятся близко. Нам больше не нужно оценивать.

    closeCriterion <- function(origin, dest) {
      manhattanDistance <- abs(origin-dest)
      #print(paste("manhattanDistance =", manhattanDistance))
      if (manhattanDistance < .1) ret <- 0 else ret <- 1
    }

    convertScore <- function(x) if (x>0) FALSE else TRUE

    for (j in 1:ncol(df)) {
      print(paste("col =",j))
      for (i in 1:nrow(df)) {
        print(paste("df row =",i))
        for (k in 1:nrow(df2)) {
          # print(paste("df2 row (and dflist column) =", k))
          distantScore <- closeCriterion(df[i,j], df2[k,j])
          #print(paste("df and dfdist row =", i, "  df2 row (and dflist column) =", k, "     distantScore = ", distantScore))
         dfdist[i,k] <- dfdist[i,k] + distantScore
         }
      }
    }

    dfdist  # have a look at the numerical results

    dfclose <- matrix(lapply(dfdist, convertScore), ncol = nrow(df2))

Я хотел посмотреть, как будет выглядеть процесс в масштабе.

    set.seed(42)
    df <- matrix(rnorm(3000), ncol = 30)
    set.seed(42)
    df2 <-matrix(rnorm(5580), ncol = 30)
    dfdist <- matrix(0, nrow = nrow(df), ncol = nrow(df2))

Затем я запустил блок кода, чтобы посмотреть, что произойдет.

~ ~ ~

Вы могли бы рассмотреть определение проблемы. Я запускал модель несколько раз, меняя критерий близости. Если запись в каждом из трех дюжин столбцов в df2 имеет 90% -ную вероятность совпадения с ее корреспондентом в df, то у строки есть только 2,2% -ная вероятность совпадения. Данные в примере не очень хороший тестовый пример для алгоритма.

Удачи

0 голосов
/ 30 июня 2018

Вы можете использовать пакет fuzzyjoin

library(fuzzyjoin)

# adding two rows that match
df2 <- rbind(df2,df[1:2,] +0.01)

df %>%
  fuzzy_left_join(df2,match_fun= function(x,y) y<x+0.1 & y> x-0.1 ) %>%
  mutate(found=!is.na(x.y)) %>%
  select(-4:-6)

# # A tibble: 5 x 4
#     x.x   y.x   z.x found
#   <dbl> <dbl> <dbl> <lgl>
# 1   1     0.5   0.5 TRUE 
# 2   1     0.8   0.7 TRUE 
# 3   0.3   0.1   1   FALSE
# 4   0.9   0.7   0.2 FALSE
# 5   0.7   0.7   0.5 FALSE

найти дополнительную информацию там: Объединение / сопоставление фреймов данных в R

...