R: цикл с повторяющимися значениями - PullRequest
0 голосов
/ 23 мая 2011

Я пытаюсь написать цикл, который будет искать нужную дату в data.frame B (date_B[j]) и скопировать соответствующее значение X_B[j] в переменную X_A[i], относящуюся к той же дате date_A[i].

Сложность заключается в том, что a) целевой data.frame A имеет несколько одинаковых дат, но b) систематически не все даты, которые имеет data.frame (B). (B) включает в себя все необходимые даты, хотя. Следовательно, кадры данных имеют разную длину.

Вопросы:

  1. Почему следующий цикл не работает, но не возвращает сообщения об ошибках?
  2. Как это исправить?
  3. Есть ли другие способы решения этой проблемы в R?

Фреймы данных:

    A =

         date_A         X_A 
    1    2010-01-01       
    2    2010-01-02
    3    2010-01-03
    4    2010-01-02
    5    2010-02-03
    6    2010-01-01
    7    2010-01-02
    .
    .
    . 
    20000

    B= 

         date_B           X_B
    1    2010-01-01       7.9
    2    2010-01-02       8.5
    3    2010-01-03       2.1
    .
    .
    400

Моя цель:

    A=

         date_A          X_A    
    1    2010-01-01      7.9 
    2    2010-01-02      8.5
    3    2010-01-03      2.1
    4    2010-01-02      8.5
    5    2010-02-03      2.1
    6    2010-01-01      7.9
    7    2010-01-02      8.5

Я написал следующий цикл, но по какой-то причине он не проходит через первый ряд. Другими словами, он не изменяет значения других ячеек X_A, хотя цикл продолжает работать бесконечно.

    i=1; j=1; 
    while (i <= nrow(A)) 
       while (j <= nrow(B)) {
          if (A$date_A[i]==B$date_B[j]) A$X_A[i] <- B$X_B[j]; 
          j <- j+1; if (j == nrow(B)) 
          i <- i+1; 
          j=1
       } 

Спасибо за вашу помощь.

Ответы [ 3 ]

7 голосов
/ 23 мая 2011

С такой проблемой merge делает это намного проще.По вашему примеру я не получаю совпадения с седьмым рядом, но, возможно, у вас была опечатка.У моего A-кадра данных был только столбец date_A.Если вы хотите переименовать столбец X_B, то names()<- сделает это легко;

 merge(A, B, by.x=1, by.y=1, all.x=TRUE)
#---result---
      date_A X_B
1 2010-01-01 7.9
2 2010-01-01 7.9
3 2010-01-02 8.5
4 2010-01-02 8.5
5 2010-01-02 8.5
6 2010-01-03 2.1
7 2010-02-03  NA
2 голосов
/ 23 мая 2011

Вау!Твой код пугает меня.По крайней мере, используйте цикл for для такого рода вещей (хотя решение @ Dwin является способом решения этой проблемы):

for(i in seq(nrow(A)))
{
    for(j in seq(nrow(B)))
    {
        if(A$date_A[i]==B$date_B[j])
        {
            A$X_A[i] <- B$X_B[j]
        }
    }
}

Это предотвратит все уродство с попыткой вручную выполнитьприращения в конце ваших циклов while (в вашем собственном коде, к слову, j = 1 нужно было перемещать за пределы внутренних скобок).

Примечание: этот код, как и ваш, не решаетошибка, когда B содержит две строки с той же датой, что и в A (для этой даты всегда будет использоваться значение последней строки в B).Он помогает вам понять вместо простых покадровых циклов.

2 голосов
/ 23 мая 2011

С этими данными:

A <- data.frame( date_A = c('2010-01-01', '2010-01-02', '2010-01-03', '2010-01-02',
    '2010-02-03', '2010-01-01', '2010-01-02') )

B <- data.frame(   
    date_B = c('2010-01-01','2010-01-02','2010-01-03'),
    X_B = c(7.9,8.5,2.1))

Вы можете использовать match() для индексации X_B значений в правильном порядке:

A$X_A <- B$X_B[match(A$date_A,B$date_B)]

match() возвращает индексы местоположений B$date_B в A$date_A. Другой прием, который нужно использовать, - это использовать уровни фактора в качестве индекса:

A$X_A <- B$X_B[A$date_A]

, который работает, потому что каждый фактор имеет уровни в отсортированном порядке и соответствует числовым значениям (1,2,3 ...). Таким образом, если B отсортировано в соответствии с этими уровнями, это возвращает правильные индексы. (вы можете отсортировать B, чтобы убедиться: B <- B[order(B$date_B),])


Что касается того, почему цикл не работает. Во-первых, я думаю, что вы действительно не хотите использовать ; в R-скриптах. Это делает код намного сложнее для чтения. Лучше всего, если вы научитесь писать понятный код. В вашем коде вы можете использовать присваиватели более согласованно и использовать правильные отступы. Например:

i <- 1
j <- 1
while (i <= nrow(A))
{
    while (j <= nrow(B))
    {
        if (A$date_A[i]==B$date_B[j]) A$X_A[i] <- B$X_B[j]
        j <- j+1
        if (j == nrow(B)) i <- i+1
        j <- 1
    }
}

Это ваш код, но его гораздо понятнее читать. Для меня это не работает, потому что уровни не сопоставимы (из-за опечатки), поэтому я вставил вызов as.character(). Это, вероятно, не требуется в реальном наборе данных.

Индексация сразу показывает самую большую проблему здесь: вы потеряли j <- 1 за пределами if (j == nrow(B)) части. Использование ; завершает строку и, следовательно, условную часть. Из-за этого j устанавливается в 1 в каждом цикле.

Изменение этого параметра улучшает его работу, но вы по-прежнему получаете ошибку, поскольку цикл while для j может не завершиться до того, как i будет больше, чем число строк в A. Это можно изменить, установив оператор AND и сворачивая оба цикла while в один. Наконец, вам нужно установить для оператора if большее количество строк в B, либо вы пропустите одну строку. Это должно работать:

i <- 1
j <- 1
while (j <= nrow(B) & i <= nrow(A))
{
    if (as.character(A$date_A[i])==as.character(B$date_B[j])) A$X_A[i] <- B$X_B[j]
    j <- j+1
    if (j > nrow(B))
    {   
        i <- i+1
        j <- 1
    }
}

Но это только для того, чтобы показать, что пошло не так, я никогда не рекомендовал делать что-то подобное таким образом. Даже если вы действительно хотите использовать циклы, вам, вероятно, лучше использовать циклы for.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...