как преобразовать маржинальные данные (данные строки отчетности) с помощью R

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

Name Line Manager
A1 B1
A2 B1
A3 B2
B1 C1
B2 C1
A4 C1

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

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

(1)

Name Line Manager
A1 B1
A2 B1
A3 B2
A1 C1
A2 C1
A3 C1
A4 C1
B1 C1
B2 C1

(2)

Name LM_Level1 LM_Level2
A1 B1 C1
A2 B1 C1
A3 B2 C1
A4 C1
B1 C1
B2 C1

person linuxiaobai    schedule 20.12.2020    source источник


Ответы (2)


Настройка данных:

dat <- tibble::tribble(~Name ,   ~Line.Manager,
"A1" , "B1",
"A2" , "B1",
"A3" , "B2",
"A1" , "C1",
"A2" , "C1",
"A3" , "C1",
"A4" , "C1",
"B1" , "C1",
"B2" , "C1")

# Reshaping to wide
dat <- tidyr::pivot_wider(dat, names_from = Line.Manager, values_from = Line.Manager)
# Moving values to the left - stolen from here: https://stackoverflow.com/a/26651706/5221626
dat[] <- t(apply(dat, 1, function(x) c(x[!is.na(x)], x[is.na(x)])))
# Removing empty columns
dat <- janitor::remove_empty(dat, "cols")
# Renaming column names.
names(dat)[2:ncol(dat)] <- paste0("LM_Level", seq_len(ncol(dat) - 1))
dat

# A tibble: 6 x 3
  Name  LM_Level1 LM_Level2
  <chr> <chr>     <chr>    
1 A1    B1        C1       
2 A2    B1        C1       
3 A3    B2        C1       
4 A4    C1        NA       
5 B1    C1        NA       
6 B2    C1        NA 

Мне кажется, что второй шаг немного неудобен, но я не могу придумать лучшего способа.

person Phil    schedule 20.12.2020
comment
Спасибо за ответ. Я попробую позже. Я думаю об использовании какого-нибудь рекурсивного метода. - person linuxiaobai; 11.01.2021

Для (2) (обозначенного здесь step_1) вы можете присоединиться к df самому себе; соедините соединение с line_manager и name.

Для (1) (помечено step_2) просто сгруппируйте по name и поверните дальше.

Выход (2):

library(tidyverse)

step_1 <- 
  df %>%
    left_join(df, by = c("line_manager" = "name"), suffix = c("", "_lvl2")) %>%
    replace_na(list(line_manager_lvl2 = ""))

  name line_manager line_manager_lvl2
1   A1           B1                C1
2   A2           B1                C1
3   A3           B2                C1
4   B1           C1                  
5   B2           C1                  
6   A4           C1                  

Выход (1):

step_2 <- 
  step_1 %>%
    group_by(name) %>%
    pivot_longer(-name, names_to = 'tmp', values_to = 'line_manager') %>%
    select(-tmp) %>%
    filter(line_manager != "")

  name  line_manager
  <chr> <chr>       
1 A1    B1          
2 A1    C1          
3 A2    B1          
4 A2    C1          
5 A3    B2          
6 A3    C1          
7 B1    C1          
8 B2    C1          
9 A4    C1     

Данные:

df <- read.table(text = "Name   Line Manager
A1  B1
A2  B1
A3  B2
B1  C1
B2  C1
A4  C1", 
                 sep = "\t", 
                 header = TRUE, 
                 stringsAsFactors = FALSE) %>%
  mutate(across(everything(), str_replace, " ", "")) %>%
  janitor::clean_names()

Примечание: я знаю, что вызывать OP (2), step_1 и (1) как step_2 немного странно, но индексы step_ больше похожи на логический порядок операций в этом преобразовании.

person andrew_reece    schedule 20.12.2020
comment
Спасибо за ответ. Я попробую позже. Я думаю об использовании какого-нибудь рекурсивного метода. - person linuxiaobai; 11.01.2021