Изменить форму данных для последовательных лет
У меня есть данные о тысячах покупателей, которые посетили магазины за последние 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
). Вот основной подход:
Трещина
Year_store
на две отдельные колонкиYear
а такжеStore
в вашем фрейме данных; на данный момент он содержит два совершенно разных вида данных, и вам действительно нужно обрабатывать их отдельно.Делать
NextYear
столбец, определенный какYear + 1
Делать
NextStore
столбец, в котором вы назначаете соответствующий код магазинаCustomer_Id
и для чегоYear
такой же, как в этом рядуNextYear
, присваиваяNA
если нет записи о том, что покупатель посетил магазин в следующем году и выдал ошибку, если данные не соответствуют требуемой спецификации (неоднозначно, какой магазин был посещен первым в следующем году).Удалите любой из рядов, в которых
NextStore
являетсяNA
и объединитьNextYear
а такжеNextStore
столбцы вNextYear_NextStore
колонка.Подведите итог вашего фрейма данных по
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!