После моего последнего поста о поиске расстояния дата / время от выходных, Хэдли Уикхем предложил мне улучшить функцию, векторизовав ее…
… Поэтому я решил попробовать векторизовать некоторые другие функции, которые я написал недавно, и показать две версии.
Я нашел следующие статьи полезными для объяснения векторизации и почему вы можете захотеть сделать это:
- Векторизация в R: почему?
- Главы 3 и 4 Р Инферно
- Резкое увеличение скорости R с помощью векторизации и исправления ошибок
Давайте начнем.
Расстояние от выходных
Мы хотим выяснить, сколько часов до выходных, т.е. ближайшей субботы / воскресенья, является конкретная дата / время. Мы будем использовать следующие библиотеки и набор даты / времени:
|
1
2
3
4
5
6
7
|
library(dplyr)library(lubridate)library(geosphere)options("scipen"=100, "digits"=4) times = ymd_hms("2002-01-01 17:00:00") + c(0:99) * hours(1)data = data.frame(time = times) |
|
1
2
3
4
5
6
7
8
|
> data %>% head() time1 2002-01-01 17:00:002 2002-01-01 18:00:003 2002-01-01 19:00:004 2002-01-01 20:00:005 2002-01-01 21:00:006 2002-01-01 22:00:00 |
Давайте сначала посмотрим на не векторизованную версию:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
distanceFromWeekend = function(dateToLookup) { before = floor_date(dateToLookup, "week") + hours(23) + minutes(59) + seconds(59) after = ceiling_date(dateToLookup, "week") - days(1) timeToBefore = dateToLookup - before timeToAfter = after - dateToLookup if(timeToBefore < 0 || timeToAfter < 0) { 0 } else { if(timeToBefore < timeToAfter) { timeToBefore / dhours(1) } else { timeToAfter / dhours(1) } }} |
Теперь давайте запустим его для нашего фрейма данных:
|
1
2
3
4
5
|
> system.time( data %>% mutate(ind = row_number()) %>% group_by(ind) %>% mutate(dist = distanceFromWeekend(time)) ) user system elapsed 1.837 0.020 1.884 |
А теперь для векторизованной версии Хэдли:
|
1
2
3
4
5
6
7
8
9
|
distanceFromWeekendVectorised = function(dateToLookup) { before = floor_date(dateToLookup, "week") + hours(23) + minutes(59) + seconds(59) after = ceiling_date(dateToLookup, "week") - days(1) pmin(pmax(dateToLookup - before, 0), pmax(after - dateToLookup, 0)) / dhours(1)} > system.time(data %>% mutate(dist = distanceFromWeekendVectorised(time))) user system elapsed 0.020 0.001 0.023 |
Извлечение даты начала
Мой следующий пример — очистка данных Google Trends и извлечение даты начала из ячейки в файле CSV.
Мы будем использовать этот фрейм данных:
|
1
2
|
googleTrends = read.csv("/Users/markneedham/Downloads/report.csv", row.names=NULL)names(googleTrends) = c("week", "score") |
|
01
02
03
04
05
06
07
08
09
10
11
12
|
> googleTrends %>% head(10) week score1 Worldwide; 2004 - present 2 Interest over time 3 Week neo4j4 2004-01-04 - 2004-01-10 05 2004-01-11 - 2004-01-17 06 2004-01-18 - 2004-01-24 07 2004-01-25 - 2004-01-31 08 2004-02-01 - 2004-02-07 09 2004-02-08 - 2004-02-14 010 2004-02-15 - 2004-02-21 0 |
Не векторизованная версия выглядела так:
|
1
2
3
4
5
6
7
8
9
|
> system.time( googleTrends %>% mutate(ind = row_number()) %>% group_by(ind) %>% mutate(dates = strsplit(week, " - "), start = dates[[1]][1] %>% strptime("%Y-%m-%d") %>% as.character()) ) user system elapsed 0.215 0.000 0.214 |
В этом случае на самом деле невозможно векторизовать код с помощью strsplit, поэтому нам нужно использовать что-то еще. Антониос показал мне, как это сделать, используя substr :
|
1
2
3
|
> system.time(googleTrends %>% mutate(start = substr(week, 1, 10) %>% ymd())) user system elapsed 0.018 0.000 0.017 |
Расчет расстояния хаверсин
Я хотел найти большое круговое расстояние от коллекции мест до центра Лондона. Я начал с этого фрейма данных:
|
01
02
03
04
05
06
07
08
09
10
11
|
centre = c(-0.129581, 51.516578)venues = read.csv("/tmp/venues.csv") > venues %>% head() venue lat lon1 Skills Matter 51.52 -0.099112 Skinkers 51.50 -0.083873 Theodore Bullfrog 51.51 -0.123754 The Skills Matter eXchange 51.52 -0.099235 The Guardian 51.53 -0.122346 White Bear Yard 51.52 -0.10980 |
Моя не векторизованная версия выглядела так:
|
1
2
3
4
5
|
> system.time(venues %>% mutate(distanceFromCentre = by(venues, 1:nrow(venues), function(row) { distHaversine(c(row$lon, row$lat), centre) })) ) user system elapsed 0.034 0.000 0.033 |
Это довольно быстро, но мы можем добиться большего — функция distHaversine позволяет нам вычислять несколько расстояний, если первый аргумент — это матрица значений lon / lat, а не вектор:
|
1
2
3
4
5
|
> system.time( venues %>% mutate(distanceFromCentre = distHaversine(cbind(venues$lon, venues$lat), centre)) ) user system elapsed 0.001 0.000 0.001 |
Один я не могу понять …
И, наконец, у меня есть функция, которую я не могу понять, как векторизовать, но, может быть, кто-то с большим умением R, чем я, может?
У меня есть фрейм данных, содержащий совокупное количество членов различных групп NoSQL London :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
cumulativeMeetupMembers = read.csv("/tmp/cumulativeMeetupMembers.csv")> cumulativeMeetupMembers %>% sample_n(10) g.name dayMonthYear n4734 Hadoop Users Group UK 2013-10-26 11444668 Hadoop Users Group UK 2013-08-03 9794936 Hadoop Users Group UK 2014-07-31 16445150 Hive London 2012-10-15 1098020 Neo4j - London User Group 2014-03-15 8267666 Neo4j - London User Group 2012-08-06 781030 Big Data London 2013-03-01 14166500 London MongoDB User Group 2013-09-21 9528290 Oracle Big Data 4 the Enterprise 2012-06-04 612584 Data Science London 2012-03-20 285 |
И я хочу узнать количество участников группы на конкретную дату. например, учитывая следующие данные …
|
01
02
03
04
05
06
07
08
09
10
11
12
|
> cumulativeMeetupMembers %>% head(10) g.name dayMonthYear n1 Big Data / Data Science / Data Analytics Jobs 2013-01-29 12 Big Data / Data Science / Data Analytics Jobs 2013-02-06 153 Big Data / Data Science / Data Analytics Jobs 2013-02-07 284 Big Data / Data Science / Data Analytics Jobs 2013-02-10 315 Big Data / Data Science / Data Analytics Jobs 2013-02-18 336 Big Data / Data Science / Data Analytics Jobs 2013-03-27 387 Big Data / Data Science / Data Analytics Jobs 2013-04-16 418 Big Data / Data Science / Data Analytics Jobs 2013-07-17 539 Big Data / Data Science / Data Analytics Jobs 2013-08-28 5810 Big Data / Data Science / Data Analytics Jobs 2013-11-11 63 |
… Количество участников группы «Большие данные / Data Science / Data Analytics» на 10 ноября 2013 года должно быть 58.
Я создал этот фрейм данных групп и случайных дат:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
dates = ymd("2014-09-01") + c(0:9) * weeks(1)groups = cumulativeMeetupMembers %>% distinct(g.name) %>% select(g.name) groupsOnDate = merge(dates, groups)names(groupsOnDate) = c('date', 'name') > groupsOnDate %>% sample_n(10) date name156 2014-10-06 GridGain London153 2014-09-15 GridGain London70 2014-11-03 Couchbase London185 2014-09-29 Hadoop Users Group UK105 2014-09-29 Data Science London137 2014-10-13 Equal Experts Technical Meetup Group360 2014-11-03 Scale Warriors of London82 2014-09-08 Data Science & Business Analytics London Meetup233 2014-09-15 London ElasticSearch User Group84 2014-09-22 Data Science & Business Analytics London Meetup |
Не векторизованная версия выглядит так:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
memberCount = function(meetupMembers) { function(groupName, date) { (meetupMembers %>% filter(g.name == groupName & dayMonthYear < date) %>% do(tail(., 1)))$n } } findMemberCount = memberCount(cumulativeMeetupMembers) > system.time(groupsOnDate %>% mutate(groupMembers = by(groupsOnDate, 1:nrow(groupsOnDate), function(row) { findMemberCount(row$name, as.character(row$date)) }) %>% cbind() %>% as.vector() )) user system elapsed 2.259 0.005 2.269 |
Вывод выглядит так:
|
01
02
03
04
05
06
07
08
09
10
11
|
date name groupMembers116 2014-10-06 DeNormalised London 157322 2014-09-08 OpenCredo Tech Workshops 771 2014-09-01 Data Enthusiasts London 233 2014-09-15 London ElasticSearch User Group 614171 2014-09-01 HPC & GPU Supercomputing Group of London 80109 2014-10-27 Data Science London 363220 2014-11-03 Big Data Developers in London 70842 2014-09-08 Big Data Week London Meetup 96127 2014-10-13 Enterprise Search London Meetup 575409 2014-10-27 Women in Data 548 |
Я испробовал много разных подходов, но не смог придумать версию, позволяющую мне передавать все строки в memberCount и вычислять количество для каждой строки за один раз.
Любые идеи / советы / советы приветствуются!
| Ссылка: | Р: Векторизация всех вещей от нашего партнера JCG Марка Нидхэма в блоге Марка Нидхэма . |