Подбирайте партнеров по дому без петли

Я пытаюсь избежать использования loop в своей попытке перекодировать переменную идентификации партнеров по дому.

hldid обозначает домохозяйство, а persid - человека в домохозяйстве. Переменная partner указывает persid партнера, а child указывает, является ли строка дочерней.

В переменной partner отсутствует persid для обоих партнеров.

Например, для hldid == 1, persid == 1 имеет значение 0 для partner, тогда как это должно быть 2.

Вот как выглядят данные:

> test
   hldid persid age sex relresp partner child
1      1      1  26   2       0       0     0
2      1      2  26   1       1       1     0
3      2      1  59   2       0       0     0
4      2      2  64   1       1       1     0
5      3      1  76   2       0       0     0
6      4      1  65   2       0       0     0
7      4      2  64   1       1       1     0
8      5      1  52   2       0       0     0
9      5      2  51   1       1       1     0
10     5      3  20   2      21       0     1
11     5      4  14   2      21       0     1
12     7      1  69   1       0       0     0
13     7      2  70   2       1       1     0

Мне удалось создать довольно уродливый цикл, однако он слишком медленный для всего набора данных.

test$partnerREC = test$partner

for(i in 1:13){
  for(j in 1:13){

    if (
      test$hldid[i] == test$hldid[i+1] & # verify if household is the same 
      (test$persid[i] == test$partner[j])
    )

    {
      test$partnerREC[i] = test$persid[j] # put the persid for each partner
    }
  }
}

> test
   hldid persid age sex relresp partner child partnerREC
1      1      1  26   2       0       0     0          2
2      1      2  26   1       1       1     0          1
3      2      1  59   2       0       0     0          2
4      2      2  64   1       1       1     0          1
5      3      1  76   2       0       0     0          0
6      4      1  65   2       0       0     0          2
7      4      2  64   1       1       1     0          1
8      5      1  52   2       0       0     0          2
9      5      2  51   1       1       1     0          1
10     5      3  20   2      21       0     1          0
11     5      4  14   2      21       0     1          0
12     7      1  69   1       0       0     0          2
13     7      2  70   2       1       1     0          1 

Есть идеи, как я могу использовать data.table для решения этой проблемы?

test = structure(list(hldid = c(1, 1, 2, 2, 3, 4, 4, 5, 5, 5, 5, 7, 
7), persid = c(1, 2, 1, 2, 1, 1, 2, 1, 2, 3, 4, 1, 2), age = c(26, 
26, 59, 64, 76, 65, 64, 52, 51, 20, 14, 69, 70), sex = c(2, 1, 
2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2), relresp = c(0, 1, 0, 1, 0, 
0, 1, 0, 1, 21, 21, 0, 1), partner = c(0, 1, 0, 1, 0, 0, 1, 0, 
1, 0, 0, 0, 1), child = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 
0)), class = "data.frame", row.names = c(NA, -13L))

В hldid == 5 только persid == 2 имеет partner == 1, остальные равны нулю. Какие значения должны принимать partner, равные 3 и 4? Почему они (partnerREC) по-прежнему равны нулю с вашим кодом?

Rui Barradas 27.11.2018 18:27

@RuiBarradas Это дети, см. Также столбец child.

Jaap 27.11.2018 18:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
91
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Возможное решение:

library(data.table)  # load the package
setDT(test)          # convert 'test' to a 'data.table'

test[, partnerREC := persid[c(pmin(2,.N):1,rep(0,(pmax(.N,2)-2)))] *
                       (persid %in% 1:2) *
                       (.N != 1)
     , by = hldid][]

который дает:

> test
    hldid persid age sex relresp partner child partnerREC
 1:     1      1  26   2       0       0     0          2
 2:     1      2  26   1       1       1     0          1
 3:     2      1  59   2       0       0     0          2
 4:     2      2  64   1       1       1     0          1
 5:     3      1  76   2       0       0     0          0
 6:     4      1  65   2       0       0     0          2
 7:     4      2  64   1       1       1     0          1
 8:     5      1  52   2       0       0     0          2
 9:     5      2  51   1       1       1     0          1
10:     5      3  20   2      21       0     1          0
11:     5      4  14   2      21       0     1          0
12:     7      1  69   1       0       0     0          2
13:     7      2  70   2       1       1     0          1

Это решение основано на предположении (полученном из данных примера), что только «persid» 1 и 2 являются партнерами, а все более высокие - дочерними.

Что это значит:

  • Группа по hldid
  • Измените порядок первых двух человек в доме, но только тогда, когда в доме больше людей с persid[c(pmin(2,.N):1,rep(0,(pmax(.N,2)-2)))], где pmin используется для обеспечения построения вектора длины 1, когда в домохозяйстве только один человек.
  • Умножьте это на (persid %in% 1:2), чтобы получить зоры для детей.
  • Умножьте это на (.N != 1), чтобы получить нулевое значение для домохозяйств из одного человека.

Я получаю это сообщение об ошибке при запуске вашего кода в моем полном образце. Error in [.data.frame(uk83_householdgrid, , : = (partner_id, persid[c(pmin(2, : unused argument (by = hldid). Есть идеи, почему?

giac 28.11.2018 15:42

@giacomo Вероятно, потому что вы не преобразовали свои данные в Таблица данных с setDT(), как показано в начале моего ответа.

Jaap 28.11.2018 16:53

Спасибо, он работает очень хорошо, это действительно классная строка кода. Не могли бы вы сломать мне еще немного этот persid[c(pmin(2,.N):1,rep(0,(pmax(.N,2)-2)))], я не уверен, что понимаю его полностью. Спасибо

giac 11.01.2019 15:13

Вы можете добраться туда, выполнив некоторые шаги dplyr, чтобы объединить данные о себе и обновить значение партнера при persid == partner.

test2 <- left_join(test, test %>% select(hldid, persid, partner) %>% filter(partner != 0), by=c("hldid")) %>%
  filter(persid.x == partner.y) %>%
  mutate(partner.x = persid.y)

Это даст вам совпадение главы семьи с идентификатором партнера, но вам придется снова присоединить это к исходным данным (я не уверен, что это эквивалент SQL update на жаргоне dplyr).

df <- data.frame(matrix(data = NA, ncol = 7))
names(df) <- names(test)

for(id in unique(test$hldid)){
  t <- test[test$hldid==id,]
  t$partner[t$persid == t$partner[t$partner!=0]] <- t$persid[which(t$partner!=0)]

  df <- rbind(df, t)
}

df <- df[-1,]

Рост объектов в цикле не считается хорошей практикой кодирования в R. См. Здесь пример того, как это влияет на производительность..

Jaap 28.11.2018 08:37

Решение на базе R более сложное, чем Jaap data.tableрешение.

Работаю с копией.

test2 <- test

После запуска кода, указанного в вопросе, выполните следующее.

test2$partnerREC <- test2$partner
sp <- split(test2, test2$hldid)
test2 <- lapply(sp, function(DF){
  i <- with(DF, which(persid %in% partner))
  j <- with(DF, which(partner %in% persid))
  #cat("i:", i, "\tj:", j, "\n")
  DF$partnerREC[i] <- DF$persid[j]
  DF
})
test2 <- do.call(rbind, test2)
row.names(test2) <- NULL

Теперь сравните оба результата.

identical(test, test2)
#[1] TRUE

1. Создайте свой тестовый data.frame.

 library(tidyverse)

 test <- tribble(
            ~hldid, ~persid, ~age, ~sex, ~relresp, ~partner, ~child,
             1,      1,     26,   2,       0,       0,         0,
             1,      2,     26,   1,       1,       1,         0,
             2,      1,     59,   2,       0,       0,         0,
             2,      2,     64,   1,       1,       1,         0,
             3,      1,     76,   2,       0,       0,         0,
             4,      1,     65,   2,       0,       0,         0,
             4,      2,     64,   1,       1,       1,         0,
             5,      1,     52,   2,       0,       0,         0,
             5,      2,     51,   1,       1,       1,         0,
             5,      3,     20,   2,      21,       0,         1,
             5,      4,     14,   2,      21,       0,         1,
             7,      1,     69,   1,       0,       0,         0,
             7,      2,     70,   2,       1,       1,         0)

2. На помощь приходят договариваться(), группа по() и мутировать ().

test %>%
  # arrange the data in case the raw data did not 
  arrange(hldid, child, persid) %>%
  # group each household 
  group_by(hldid) %>%
  # match first and second household person as each other's partner
  mutate(partnerREC = ifelse(persid == first(persid), nth(persid, 2), first(persid))) %>%
  # correct partnerREC for child and single 
  mutate(partnerREC = ifelse(child == 1 | is.na(partnerREC), 0, partnerREC))
  # un-group it
  ungroup() 

Решение loop с использованием rcpp

Исходный скрипт rcpp

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector HHgrid(CharacterVector hid, NumericVector persid, NumericVector partner, 
NumericVector partnerRec) {

  int nrows = hid.size();

  for (int i = 1; i < nrows - 1; i ++){
    for (int j = 0; j < nrows - 1; j++){

      if ( (hid(i) == hid(i+1)) & ( persid(i) == partner(j) ) ){
        partnerRec(i) = persid(j); 
      }
    }
    Rcout << i << std::endl;     
  }
  return(partnerRec); 
}

и запустите функцию

HHgrid(hid = test$hldid, persid = test$persid, partner = test$partner, partnerRec = test$partnerRec)

В первом случае есть небольшая проблема (если вы знаете, как ее исправить)

Другие вопросы по теме