2014-08-28 1 views
0

Я ищу быстрый и эффективный способ вычисления проблемы, описанной ниже. Любая помощь будет оценена, спасибо заранее!Правильный способ векторизации функции «поиска»

У меня есть пара очень больших файлов csv, которые имеют различную информацию об одном и том же объекте, но в моем окончательном вычислении мне нужны все атрибуты в другой таблице. Я пытаюсь рассчитать нагрузку большого количества электрических подстанций, сначала у меня есть список уникальных электрических подстанций;

Unique_Substations <- data.frame(Name = c("SubA", "SubB", "SubC", "SubD")) 

В другом списке у меня есть информация о клиентах, стоящих за этими подстанциями;

Customer_Information <- data.frame(
    Customer = 1001:1010, 
    SubSt_Nm = sample(unique(Unique_Substations$Name), 10, replace = TRUE), 
    HouseHoldType = sample(1:2, 10, replace = TRUE) 
) 

И в другом списке у меня есть информация о, скажем, солнечных батареях на этих крышах клиентов (на разные годы);

Solar_Panels <- data.frame(
    Customer = sample(1001:1010, 10, replace = TRUE), 
    SolarPanelYear1 = sample(10:20, 10, replace = TRUE), 
    SolarPanelYear2 = sample(15:20, 10, replace = TRUE) 
) 

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

SolarLoad <- data.frame(Load = c(0, -10, -10, 5)) 
HouseHoldLoad <- data.frame(Type1 = c(1, 3, 5, 2), Type2 = c(3, 5, 6, 1)) 

Так что теперь мне нужно сопоставить эти списки;

ML_SubSt_Cust <- sapply(Unique_Substations$Name, 
         function(x) which(Customer_Information$SubSt_Nm %in% x == TRUE)) 

ML_Cust_SolarP <- sapply(Customer_Information$Customer, 
         function(x) which(Solar_Panels$Customer %in% x == TRUE)) 

(Здесь я использую метод which(xxx %in% x == TRUE), потому что мне нужно несколько матчей и match() возвращает только один матч

А теперь мы подошли к моей большой вопрос (но, вероятно, не единственная моя проблема с этим методом), наконец, Я хочу рассчитать максимальную нагрузку на каждую подстанцию ​​за каждый год. Для этого я сначала написал цикл for, который зациклился на списке Unique_Substations, который, конечно, очень неэффективен. После этого я попытался ускорить его, используя outer(), но Я не думаю, что у меня была правильная векторная функция. Моя максимальная функция выглядит следующим образом (я только написал ее для части солнечной панели, чтобы сохранить i t простой);

GetMax <- function(i, Yr) { 
    max(sum(Solar_Panels[unlist(ML_Cust_SolarP[ML_SubSt_Cust[[i]]], use.names= FALSE),Yr])*SolarLoad) 
} 

Я уверен, что это неэффективно, но я понятия не имею, как это сделать любым другим способом.

Для получения окончательных результатов я использую внешнюю функцию;

Results <- outer(1:nrow(Unique_Substations), 1:2, Vectorize(GetMax)) 

В моем примере все эти данные кадры очень намного больше (40000 строк каждый или около того), так что я действительно нужна хорошая оптимизация функций, участвующих. Я пытался подумать о способах векторизации функции, но я не мог ее решить. Любая помощь будет оценена по достоинству.

EDIT:

Теперь, когда я полностью понимаю, принятый awnser у меня есть еще одна проблема. Мой фактический Customer_Information составляет 188 тыс. Строк в длину, а мой фактический HouseHoldLoad составляет 53 тыс. Строк. Излишне говорить, что это не merge() очень хорошо. Есть ли другое решение этой проблемы, которое не требует merge() или для слишком медленных циклов?

+0

с одной стороны, вам не нужно 'data.frames', так как есть только один тип данных каждый объект. Векторов и матриц будет достаточно. Далее, а не 'which (foo% in% bar == TRUE)', просто 'which (bar == foo)' (где 'foo' - это скаляр, а' bar' - ваш вектор или матрица). –

+1

@CarlWitthoft 'который (foo% in% bar == TRUE)' такой же, как 'which (foo% in% bar)', а не как 'which (bar == foo)'. Возьмите 'bar = c (0,1)' и 'foo = c (1,0)', и разница очевидна. Я согласен, что вы можете использовать либо, когда 'foo' содержит только один элемент, но они не совпадают. –

+0

@JorisMeys Спасибо - хорошая точка –

ответ

2

Первый: set.seed() при генерации случайных данных! Я сделал set.seed(1000) перед кодом для этих результатов.

Я думаю, что это может быть немного merge -ing и dplyr. Во-первых, мы получаем данные в лучшей форме:

library(dplyr) 
library(reshape2) 

HouseHoldLoad <- melt(HouseHoldLoad, value.name="Load") %>% 
    select(HouseHoldType=variable, Load) %>% 
    mutate(HouseHoldType=gsub("Type", "", HouseHoldType)) 

Solar_Panels <- melt(Solar_Panels, id.vars="Customer", 
        value.name="SPYearVal") %>% 
    select(Customer, SolarPanelYear=variable, SPYearVal) %>% 
    mutate(SolarPanelYear=gsub("SolarPanelYear", "", SolarPanelYear)) 

dat <- merge(Customer_Information, Solar_Panels, by="Customer") 

Это дает нам:

## Customer SubSt_Nm HouseHoldType SolarPanelYear SPYearVal 
## 1  1001  SubB    1    1  16 
## 2  1001  SubB    1    2  18 
## 3  1001  SubB    1    2  16 
## 4  1001  SubB    1    1  20 
## 5  1002  SubD    2    1  16 
## 6  1002  SubD    2    1  13 
## 7  1002  SubD    2    2  20 
## 8  1002  SubD    2    2  18 
## 9  1003  SubA    1    2  15 
## 10  1003  SubA    1    1  16 
## 11  1005  SubC    2    2  19 
## 12  1005  SubC    2    1  10 
## 13  1006  SubA    1    1  15 
## 14  1006  SubA    1    2  19 
## 15  1007  SubC    1    1  17 
## 16  1007  SubC    1    2  19 
## 17  1009  SubA    1    1  10 
## 18  1009  SubA    1    1  18 
## 19  1009  SubA    1    2  18 
## 20  1009  SubA    1    2  18 

Теперь мы просто группа и итог:

dat %>% group_by(SubSt_Nm, SolarPanelYear) %>% 
    summarise(mx=max(sum(SPYearVal)*SolarLoad)) 

## SubSt_Nm SolarPanelYear mx 
## 1  SubA    1 295 
## 2  SubA    2 350 
## 3  SubB    1 180 
## 4  SubB    2 170 
## 5  SubC    1 135 
## 6  SubC    2 190 
## 7  SubD    1 145 
## 8  SubD    2 190 

Если вы используете data.table против фреймов данных , он должен быть довольно быстрым даже с 40K записей.

UPDATE Для тех, кто не может установить dplyr, это просто использует reshape2 (надеюсь, что устанавливаемое)

library(reshape2) 

HouseHoldLoad <- melt(HouseHoldLoad, value.name="Load") 
colnames(HouseHoldLoad) <- c("HouseHoldType", "Load") 
HouseHoldLoad$HouseHoldType <- gsub("Type", "", HouseHoldLoad$HouseHoldType) 

Solar_Panels <- melt(Solar_Panels, id.vars="Customer", value.name="SPYearVal") 
colnames(Solar_Panels) <- c("Customer", "SolarPanelYear", "SPYearVal") 
Solar_Panels$SolarPanelYear <- gsub("SolarPanelYear", "", Solar_Panels$SolarPanelYear) 

dat <- merge(Customer_Information, Solar_Panels, by="Customer") 

rbind(by(dat, list(dat$SubSt_Nm, dat$SolarPanelYear), function(x) { 
    mx <- max(sum(x$SPYearVal) * SolarLoad) 
})) 

##  1 2 
## SubA 295 350 
## SubB 180 170 
## SubC 135 190 
## SubD 145 190 

Если вы действительно не может установить даже reshape2, то работает только с базой stats пакет:

colnames(HouseHoldLoad) <- c("Load.1", "Load.2") 
HouseHoldLoad <- reshape(HouseHoldLoad, varying=c("Load.1", "Load.2"), direction="long", timevar="HouseHoldType")[1:2] 

colnames(Solar_Panels) <- c("Customer", "SolarPanelYear.1", "SolarPanelYear.2") 
Solar_Panels <- reshape(Solar_Panels, varying=c("SolarPanelYear.1", "SolarPanelYear.2"), direction="long", timevar="SolarPanelYear")[1:2] 
colnames(Solar_Panels) <- c("Customer", "SPYearVal") 
Solar_Panels$SolarPanelYear <- gsub("^[0-9]+\\.", "", rownames(Solar_Panels)) 

dat <- merge(Customer_Information, Solar_Panels, by="Customer") 

rbind(by(dat, list(dat$SubSt_Nm, dat$SolarPanelYear), function(x) { 
    mx <- max(sum(x$SPYearVal) * SolarLoad) 
})) 

##  1 2 
## SubA 295 350 
## SubB 180 170 
## SubC 135 190 
## SubD 145 190 
+0

Спасибо за этот ответ, но почему-то у моего рабочего компьютера возникла проблема с установкой пакета 'dplyr'. Кроме того, этот скрипт будет запущен на удаленном сервере, где я, к сожалению, не могу установить пакеты, есть ли способ сделать это без dplyr? –

+0

Мне приходилось иметь дело с подобными ситуациями (не забавно быть застрявшими с ограниченными возможностями). В ответе есть два похожих решения. Я не делал никаких таймингов, но я должен верить, что они в порядке быстрее всего медленнее, особенно на большом наборе данных. Кроме того, если вам удастся использовать 'dplyr', вы могли бы даже нажать на базу данных и потребовать экстракт (который я предполагаю, что вы работаете). – hrbrmstr

+0

Все спасибо! Я дам ему попробовать и принять, когда это сработает! –