Изменить форму данных для последовательных лет

У меня есть данные о тысячах покупателей, которые посетили магазины за последние 3 года. Для каждого клиента у меня есть:

  • Я БЫ
  • Сочетание года и первый магазин посетил в этом году.
Customer_Id | Year_*_Store 
1            2010_A
1            2011_B
1            2012_C
2            2010_A
2            2011_B
2            2012_D

То, что я хотел бы иметь, - это следующая структура данных, чтобы визуализировать эволюцию поведения клиентов с речным участком (он же участок Сэнки)

Например, 2 покупателя, которые впервые посетили магазин A в 2010 году, впервые посетили магазин B в 2011 году:

SOURCE |     TARGET |   NB_CUSTOMERS
2010_A      2011_B      2
2011_B      2012_C      1
2011_B      2012_D      1

Я не хочу ссылки между двумя годами, которые не являются последовательными, как 2010_A и 2012_D

Как я могу сделать это в R?

2 ответа

Решение

Обратите внимание, что вы не можете иметь * в названиях столбцов (см. ?make.names). Вот основной подход:

  1. Трещина Year_store на две отдельные колонки Year а также Store в вашем фрейме данных; на данный момент он содержит два совершенно разных вида данных, и вам действительно нужно обрабатывать их отдельно.

  2. Делать NextYear столбец, определенный как Year + 1

  3. Делать NextStore столбец, в котором вы назначаете соответствующий код магазина Customer_Id и для чего Year такой же, как в этом ряду NextYear, присваивая NA если нет записи о том, что покупатель посетил магазин в следующем году и выдал ошибку, если данные не соответствуют требуемой спецификации (неоднозначно, какой магазин был посещен первым в следующем году).

  4. Удалите любой из рядов, в которых NextStore является NAи объединить NextYear а также NextStore столбцы в NextYear_NextStore колонка.

  5. Подведите итог вашего фрейма данных по Year_store а также NextYear_NextStore столбцы, например, используя ddply в plyr пакет.

Некоторые примеры данных:

# same example data as question
customer.df <- data.frame(Customer_Id = c(1, 1, 1, 2, 2, 2),
    Year_Store = c("2010_A", "2011_B", "2012_C", "2010_A", "2011_B", "2012_D"),
    stringsAsFactors = FALSE)

# alternative data should throw error, customer 2 is inconsistent in 2011
badCustomer.df <- data.frame(Customer_Id = c(1, 1, 1, 2, 2, 2),
    Year_Store = c("2010_A", "2011_B", "2012_C", "2010_A", "2011_B", "2011_D"),
    stringsAsFactors = FALSE)

И реализация:

require(plyr)

splitYearStore <-  function(df) {
    df$Year <- as.numeric(substring(df$Year_Store, 1, 4))
    df$Store <- as.character(substring(df$Year_Store, 6))
    return(df) 
}

findNextStore <- function(df, matchCust, matchYear) {
    matchingStore <- with(df,
        df[Customer_Id == matchCust & Year == matchYear, "Store"])
    if (length(matchingStore) == 0) {
        return(NA)
    } else if (length(matchingStore) > 1) {
        errorString <- paste("Inconsistent store results for customer",
            matchCust, "in year", matchYear)
        stop(errorString)
    } else {
        return(matchingStore)
    }
}

tabulateTransitions <-  function(df) {
    df <- splitYearStore(df)
    df$NextYear <- df$Year + 1
    df$NextStore <- mapply(findNextStore, matchCust = df$Customer_Id,
        matchYear = df$NextYear, MoreArgs = list(df = df)) 
    df$NextYear_NextStore <- with(df, paste(NextYear, NextStore, sep = "_"))
    df <- df[!is.na(df$NextStore),]
    df <- ddply(df, .(Source = Year_Store, Target = NextYear_NextStore),
        summarise, No_Customers = length(Customer_Id))
    return(df) 
}

Результаты:

> tabulateTransitions(customer.df)
  Source Target No_Customers
1 2010_A 2011_B            2
2 2011_B 2012_C            1
3 2011_B 2012_D            1
> tabulateTransitions(badCustomer.df)
Error in function (df, matchCust, matchYear)  : 
  Inconsistent store results for customer 2 in year 2011

Не было предпринято никаких попыток оптимизации; если ваш набор данных огромен, то, возможно, вам следует изучить data.table решение.

Я бы сделал это с помощью dplyr (быстрее)

df<-read.table(header=T,text="Customer_Id  Year_Store 
1            2010_A
1            2011_B
1            2012_C
2            2010_A
2            2011_B
2            2012_D")

require(dplyr)             # for aggregation
require(riverplot)         # for Sankey

targets<-
group_by(df,Customer_Id) %.%           # group by Customer
mutate(source=Year_Store,target=c(as.character(Year_Store)[-1],NA)) %.%   # add a lag to show the shift
filter(!is.na(target)) %.%                                                # filter out empty edges
regroup(list("source","target")) %.%                                      # regroup by source & target
summarise(len=length(Customer_Id)) %.%                                    # count customers for relationship
mutate(step=as.integer(substr(target,1,4))-as.integer(substr(source,1,4))) %.%   # add a step to show how many years
filter(step==1)                                                            # filter out relationships for non consec years

topnodes <- c(as.character(unique(df$Year_Store)))                         # unique nodes

nodes <- data.frame( ID=topnodes,                                          # IDs
                   x=as.numeric(substr(topnodes,1,4)),                     # x value for plot
                   col= rainbow(length(topnodes)),                         # color each different
                   labels= topnodes,                                       # labels
                   stringsAsFactors= FALSE )

edges<-                                                                    # create list of list 
  lapply(unique(targets$source),function(x){
      l<-as.list(filter(targets,source==x)$len)                            # targets per source
      names(l)<-filter(targets,source==x)$target                           # name of target
      l
  })

names(edges)<-unique(targets$source)                                       # name top level nodes

r <- makeRiver( nodes, edges)                                              # make the River 
plot( r )                                                                  # plot it!

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