Условное изменение cumsum dlpyr

У меня есть города (от A до D), в которых проживает разное население и они находятся на разном расстоянии. Цель состоит в том, чтобы сложить общее население, живущее в радиусе круга (расстояние XY), где X - город в центре круга, а Y - любой другой город.

В этом коде:

    Df <- structure(list(Town_From = c("A", "A", "A", "B", "B", "C"), Town_To = c("B", 
    "C", "D", "C", "D", "D"), Distance = c(10, 5, 18, 17, 20, 21)), .Names = c("Town_From", 
    "Town_To", "Distance"), row.names = c(NA, -6L), class = "data.frame")

    Df2 <- structure(list(Town = c("A", "B", "C", "D"), Population = c(1000, 
    800, 500, 200)), .Names = c("Town", "Population"), row.names = c(NA, 
    -4L), class = "data.frame")

    Df <- Df %>% left_join(Df2,by=c("Town_From"="Town")) %>% 
      left_join(Df2,by=c("Town_To"="Town"))%>%
      group_by(Town_From) %>% 
      arrange(Distance)
    colnames(Df)[4]<-c("pop_TF")
    colnames(Df)[5]<-c("pop_TT")
Source: local data frame [6 x 5]
Groups: Town_From [3]

  Town_From Town_To Distance pop_TF pop_TT
      <chr>   <chr>    <dbl>  <dbl>  <dbl>
1         A       C        5   1000    500
2         A       B       10   1000    800
3         B       C       17    800    500
4         A       D       18   1000    200
5         B       D       20    800    200
6         C       D       21    500    200

города были организованы (Town_From) и упорядочены (расстояние).

В пределах круга радиусом 5 км (от А до С) живут 1000 (в А) + 500 (в В) = 1500 человек; в следующем круге живут 1500 + 800 (в Б) = 2300. В третьем круге по-прежнему проживает 2300 человек, потому что города A, B, C находятся в радиусе круга от B до C = 17 км. В радиусе круга от A до D = 18 км проживает 2300 + 200 (в D) = 2500 человек.

Вот визуализация рассматриваемых кругов. Теоретически круги могут расширяться до любого произвольного радиуса. На практике мне нужно проверить их только на расстояниях между парами городов (мест, где меняются счета).

введите описание изображения здесь


person JPV    schedule 18.01.2017    source источник
comment
Ваша цель - вычислить сумму населения как функцию расстояния от каждого города (центр круга)? Если это так, то мы можем (i) сгруппировать по каждому Town_From, (ii) отсортировать каждый из них по Distance, а затем (iii) вычислить cumsum.   -  person aichao    schedule 18.01.2017
comment
Не могли бы вы опубликовать ожидаемый результат?   -  person akrun    schedule 18.01.2017
comment
Учитывая ответ @aichao, ясно, что в вашем вопросе есть некоторая двусмысленность. Где сосредоточены ваши круги? Я понял, что каждый город должен быть центром своего собственного набора кругов. @ aichao, похоже, работал напрямую с форматом созданных вами данных. Ваш ответ, кажется, объединяет круг с центром в точке A с радиусом 17 км и расстоянием от B до C (также 17 км), в то время как, если бы города были на одной линии, (от A до B) + (от B до C) могло быть ›17 км. . Это чтение подразумевает, что вы хотите включить любой город, который находится в пределах X км от любого другого города (не обязательно в пределах одного круга).   -  person Mark Peterson    schedule 18.01.2017
comment
Привет @aichao, спасибо за вопрос. Этот вопрос очень похож на тот, на который вы ответили ранее, и был отмечен как решенный, поэтому я взял те же данные, что и в этом вопросе. Разница в том, что некоторые города на расстоянии не были добавлены к общей численности населения с помощью cumsum. Это именно то, чего мне здесь нужно избегать, и я думаю, что Марк нашел способ сделать это. Спасибо, айчао!   -  person JPV    schedule 19.01.2017
comment
Конечно, C исключен в круг с центром в B с радиусом 10 км - это 17 км от B. Представьте себе такое расположение C-A-B, где каждый - составляет 4 км. От A все три города находятся в пределах 5 км. Но из B только A и B. (есть отдельная проблема, состоящая в том, что выдуманные расстояния в данных вашего примера не совсем отражают возможную реальность.) Если вы хотите задать новый вопрос, сделайте это. Не меняйте то, о чем вы просите, и не принимайте ответ.   -  person Mark Peterson    schedule 20.01.2017
comment
Вот карта, иллюстрирующая мою точку зрения: i.imgur.com/ZpTUVER.png. Обратите внимание, что все круги имеют одинаковый радиус, но в то время как круг с центром в A содержит A, B и C, круги B и C содержат только A и самих себя. Если вы хотите чего-то другого, кроме этих кругов (именно это и задавал ваш исходный вопрос), нарисуйте это на этой карте, задайте новый вопрос и верните этот. (Обратите внимание, что эта компоновка карты максимально приближена к попарным расстояниям в исходном посте. Расстояние B-C, равное 17, невозможно, поскольку B-A + A-C равно 10 + 5 = 15.)   -  person Mark Peterson    schedule 20.01.2017
comment
Я откатил это назад, чтобы удалить необъяснимое изменение из OP и добавить описание поведения круга, которое соответствует оригиналу. Я пытался заставить ОП объяснить, но он исчез на последние 3 недели.   -  person Mark Peterson    schedule 08.02.2017


Ответы (2)


Для этого будет проще, если вы можете поместить свои данные в формат, в котором каждый город представлен на каждом «конце» расстояния (как до, так и от). Итак, я изменил внесенное вами в конце изменение на Df вместо этого. Обратите внимание, что он использует complete из tidyr.

Df_full <-
  Df %>%
  bind_rows(
    select(Df, Town_From = Town_To, Town_To = Town_From, Distance)
  ) %>%
  complete(Town_From, Town_To, fill = list(Distance = 0)) %>%
  left_join(Df2, c("Town_To" = "Town"))

Это изменяет отношение «откуда» на обратное и добавляет его в конец списка. Затем он использует complete, чтобы добавить город как свой собственный «Кому» (например, от А до А). Наконец, он присоединяется к популяции, но теперь их нужно добавить только один раз. Вот новые данные:

# A tibble: 16 × 4
   Town_From Town_To Distance Population
       <chr>   <chr>    <dbl>      <dbl>
1          A       A        0       1000
2          A       B       10        800
3          A       C        5        500
4          A       D       18        200
5          B       A       10       1000
6          B       B        0        800
7          B       C       17        500
8          B       D       20        200
9          C       A        5       1000
10         C       B       17        800
11         C       C        0        500
12         C       D       21        200
13         D       A       18       1000
14         D       B       20        800
15         D       C       21        500
16         D       D        0        200

Затем мы устанавливаем пороговые значения, которые хотим исследовать. В своем вопросе вы подразумеваете, что хотите использовать каждое из уникальных попарных расстояний. Если вы предпочитаете какой-либо другой набор для производственного использования, просто введите его здесь.

radiusCuts <-
  Df_full$Distance %>%
  unique %>%
  sort

Затем мы создаем команду sum, которая суммирует только парные города в пределах радиуса, задавая имена в процессе, чтобы упростить использование summarise_ за мгновение.

forPops <-
  radiusCuts %>%
  setNames(paste("Pop within", ., "km")) %>%
  lapply(function(x){
    paste("sum(Population[Distance <=", x,"])")
  })

Наконец, мы group_by Town_From и передаем эти сконструированные аргументы стандартной функции оценки summarise_, которая создаст каждый из столбцов в forPops:

Df_full %>%
  group_by(Town_From) %>%
  summarise_(.dots = forPops)

дает:

# A tibble: 4 × 8
  Town_From `Pop within 0 km` `Pop within 5 km` `Pop within 10 km` `Pop within 17 km` `Pop within 18 km` `Pop within 20 km` `Pop within 21 km`
      <chr>             <dbl>             <dbl>              <dbl>              <dbl>              <dbl>              <dbl>              <dbl>
1         A              1000              1500               2300               2300               2500               2500               2500
2         B               800               800               1800               2300               2300               2500               2500
3         C               500              1500               1500               2300               2300               2300               2500
4         D               200               200                200                200               1200               2000               2500

Что должно дать вам все желаемые пороги.

person Mark Peterson    schedule 18.01.2017
comment
Привет, @Mark Peterson, спасибо за ответ. Это похоже на результат, который я ищу. Прежде чем пометить его как решенный, функция complete, которую вы использовали для генерации данных, является частью пакета dplyr? - R постоянно говорит мне, что не может его найти. Приношу свои извинения, если это слишком просто, но я не являюсь ежедневным пользователем R. Спасибо! -EDIT: Я нашел это. Complete - это функция пакета tidyr. - person JPV; 19.01.2017
comment
Хороший улов, и извините, что я пропустил tidyr зависимость. Я обычно загружаю tidyverse, при котором автоматически загружаются некоторые из этих пакетов. Отредактировал сейчас. - person Mark Peterson; 19.01.2017
comment
Спасибо, Марк. Могу я спросить вас напоследок? - Если я хочу дисконтировать население в Town_From и Town_to, от населения в пределах расстояния x, должен ли я делать это с помощью команды {sum}? - person JPV; 19.01.2017
comment
Я не уверен, что вы имеете в виду. Если вы имеете в виду, что хотите исключить население города, в котором находится ваш круг, исключите аргумент complete. Если вы хотите исключить город на краю радиуса (например, город B от точки A на расстоянии 10 миль), измените значение с <= на просто < в forPops. Если что-то посложнее, вы можете задать новый вопрос. - person Mark Peterson; 19.01.2017
comment
Это именно то, но исключая одновременно город, в котором находится круг, и город на краю. ваша рекомендация сработала отлично. Спасибо! - person JPV; 19.01.2017

Если ваша цель - вычислить сумму населения как функцию увеличения расстояния от каждого города (в центре круга), то мы можем (i) сгруппировать по Town_From, (ii) отсортировать каждую из этих групп по Distance, а затем (iii) вычислить cumsum. Использование dplyr:

library(dplyr)
res <- Df %>% group_by(Town_From) %>% 
              arrange(Distance) %>% 
              mutate(sumPop=pop_TF+cumsum(pop_TT))

Используя ваши данные, результат:

print(res)
##Source: local data frame [6 x 6]
##Groups: Town_From [3]
##
##  Town_From Town_To Distance pop_TF pop_TT sumPop
##    <chr>   <chr>    <dbl>  <dbl>  <dbl>  <dbl>
##1         A       C        5   1000    500   1500
##2         A       B       10   1000    800   2300
##3         B       C       17    800    500   1300
##4         A       D       18   1000    200   2500
##5         B       D       20    800    200   1500
##6         C       D       21    500    200    700
person aichao    schedule 18.01.2017
comment
Не следует ли в круг от C до D на 21 милю также включать популяции A и B (которые находятся в 5 и 17 милях от C, соответственно)? - person Mark Peterson; 18.01.2017
comment
@MarkPeterson Да, чтобы сделать это правильно, входные данные должны отражать симметрию в том смысле, что должны быть данные от C до B и от C до A. Тогда приведенный выше код будет работать должным образом. То есть в логике кода нет ничего плохого, правильными должны быть входные данные. Другая точка зрения состоит в том, что данные накладывают ограничение на вычисление общей численности населения из C, опуская данные из C в B и A. Если вы категорически против этой точки зрения, я удалю. - person aichao; 18.01.2017
comment
Хорошо, тогда я думаю, что мы согласны больше, чем я думал, когда впервые прочитал ваш вопрос. Если я применяю логику вашего кода к симметричным данным, которые я сгенерировал в своем ответе (например, Df_full %>% group_by(Town_From) %>% arrange(Town_From, Distance) %>% mutate(sumPop=cumsum(Population))), он правильно дает каждое из этих отсечений (расположение Town_From только для отображения). Однако вы столкнетесь с некоторыми проблемами, если есть два города, которые находятся на одинаковом расстоянии от одного города (например, если город E также находится в 18 милях от A). - person Mark Peterson; 18.01.2017
comment
@MarkPeterson Да, я согласен, тогда проблема заключается в использовании cumsum. - person aichao; 18.01.2017