棘手的条件插补,理想情况下使用Tidyverse

棘手的条件插补,理想情况下使用Tidyverse,r,tidyverse,aggregation,imputation,R,Tidyverse,Aggregation,Imputation,我有一个问题,我需要对缺失的值进行一些棘手的条件插补,同时标记这些插补值,但我不太明白如何处理它 我的数据是整齐(长)的格式。我想做的是生成一个完整的数据集,其中每个“州”都有一组完整的行,其中包含“男性”、“女性”和“总数”的“出生”值。如果某一州缺少“总计”,则该“州”的“总计”由“男性”+“女性”估算。如果我们有“总计”,但没有“男性”或“女性”,则缺少的“出生”值是根据“总计”-“男性”(或“女性”,取决于缺少的值) 但是,只有当该状态的所有当前行的“源”相同时,才能插补缺少的值我们不能

我有一个问题,我需要对缺失的值进行一些棘手的条件插补,同时标记这些插补值,但我不太明白如何处理它

我的数据是整齐(长)的格式。我想做的是生成一个完整的数据集,其中每个“州”都有一组完整的行,其中包含“男性”、“女性”和“总数”的“出生”值。如果某一州缺少“总计”,则该“州”的“总计”由“男性”+“女性”估算。如果我们有“总计”,但没有“男性”或“女性”,则缺少的“出生”值是根据“总计”-“男性”(或“女性”,取决于缺少的值)

但是,只有当该状态的所有当前行的“源”相同时,才能插补缺少的值我们不能基于合并来自不同来源的数据进行插补。最后,所有插补行应具有其父状态和来源,并且二进制“聚合”列应具有“1”标志

reprex在下面,期望的结果示例在下面,并附有快速解释。如果可能的话,我想用Tidyverse来做这件事,但我愿意接受更好的解决方案。提前谢谢你

sex <- c("Male", "Female", "Total", "Male", "Female", "Male", "Female", "Male", "Total") 
state <- c("New Jersey", "New Jersey", "New Jersey", "Vermont", "Vermont", "Washington", "Washington", "Montana", "Montana")
source <- c("WHO", "WHO", "WHO", "CDC", "CDC", "UN", "CDC", "UN", "UN")
aggregated <- c(0, 0, 0, 0, 0, 0, 0, 0, 0)
births <- c(20, 30, 50, 15, 16, 20, 27, 15, 33)

df <- data.frame(sex, state, source, aggregated, births)
df
     sex      state source aggregated births
1   Male New Jersey    WHO          0     20
2 Female New Jersey    WHO          0     30
3  Total New Jersey    WHO          0     50
4   Male    Vermont    CDC          0     15
5 Female    Vermont    CDC          0     16
6   Male Washington     UN          0     20
7 Female Washington    CDC          0     27
8   Male    Montana     UN          0     15
9  Total    Montana     UN          0     33

更新03 现在我可以休息了

我知道这与亲爱的@akrun提出的两个绝妙解决方案相比算不了什么。但我不能在这里留下一个不产生预期输出的解决方案。因此,我做了一些修改,结果如下:在
出生
列中的
男性
值缺失的情况下,我扩展了代码

library(dplyr)
library(tidyr)

df %>%
  pivot_wider(names_from = sex, values_from = births) %>%
  pivot_longer(Male:Total, names_to = "sex", values_to = "births") %>%
  group_split(state, source) %>% 
  map_dfr(~ if(sum(is.na(.x$births)) > 1 ) drop_na(.x) else .x) %>%
  group_by(state, source) %>%
  mutate(aggregated = ifelse(is.na(births), 1, 0),
         births = ifelse(sex == "Female" & is.na(births), births[sex == "Total"] - 
                           births[sex == "Male"], 
                         ifelse(sex == "Total" & is.na(births), 
                                births[sex == "Female"] + births[sex == "Male"], 
                                ifelse(sex == "Male" & is.na(births), 
                                       births[sex == "Total"] - births[sex == "Female"], 
                                       births)))) %>%
  relocate(state, source, sex)


# A tibble: 11 x 5
# Groups:   state, source [5]
   state      source sex    aggregated births
   <chr>      <chr>  <chr>       <dbl>  <dbl>
 1 Montana    UN     Male            0     15
 2 Montana    UN     Female          1     18
 3 Montana    UN     Total           0     33
 4 New Jersey WHO    Male            0     20
 5 New Jersey WHO    Female          0     30
 6 New Jersey WHO    Total           0     50
 7 Vermont    CDC    Male            0     15
 8 Vermont    CDC    Female          0     16
 9 Vermont    CDC    Total           1     31
10 Washington CDC    Female          0     27
11 Washington UN     Male            0     20

更新02

亲爱的@akrun提供了另一个伟大的解决方案:


df %>% 
  group_by(state, source) %>% 
  complete(sex = unique(df$sex)) %>% 
  arrange(state, source, factor(sex, levels = c('Male', 'Female', 'Total'))) %>% 
  filter(sum(is.na(aggregated)) > 1 & !is.na(aggregated)|sum(is.na(aggregated)) <= 1) %>% 
  mutate(aggregated = replace(aggregated, is.na(aggregated), 1), 
         births = case_when(is.na(births) &  row_number() == n() ~ sum(births, na.rm = TRUE), 
                            is.na(births) ~ last(births) - na.omit(births)[1], TRUE ~ births))

# A tibble: 11 x 5
# Groups:   state, source [5]
   state      source sex    aggregated births
   <chr>      <chr>  <chr>       <dbl>  <dbl>
 1 Montana    UN     Male            0     15
 2 Montana    UN     Female          1     18
 3 Montana    UN     Total           0     33
 4 New Jersey WHO    Male            0     20
 5 New Jersey WHO    Female          0     30
 6 New Jersey WHO    Total           0     50
 7 Vermont    CDC    Male            0     15
 8 Vermont    CDC    Female          0     16
 9 Vermont    CDC    Total           1     31
10 Washington CDC    Female          0     27
11 Washington UN     Male            0     20


df%>%
分组依据(州、来源)%>%
完成(性别=唯一(df$sex))%>%
排列(状态、来源、因素(性别、等级=c(‘男性’、‘女性’、‘总数’)))%>%
过滤器(总和(is.na(聚合))>1&!is.na(聚合)|总和(is.na(聚合))%
突变(聚合=替换(聚合,为.na(聚合),1),
出生率=案例时(is.na(出生率)&行数()==n()~sum(出生率,na.rm=TRUE),
is.na(出生)~最后一次(出生)-na.省略(出生)[1],TRUE~出生)
#A tibble:11 x 5
#分组:国家,来源[5]
国家来源性别合计出生数
1蒙大拿州联合国男0 15
2蒙大拿州联合国女1 18
3蒙大拿州联合国总计0 33
4新泽西州男子0 20
5新泽西州女性0 30
6新泽西州,共0.50人
7佛蒙特州疾病预防控制中心男性0 15
8佛蒙特州疾病预防控制中心女性0 16
9佛蒙特州疾病预防控制中心总计1 31
10华盛顿疾控中心女性0 27
11华盛顿联合国男0 20

您可以执行
df%>%group_split(state,source)%>%map_-dfr(~if(所有(c('Male','femal')%in%.x$sex)&&!'Total'%in%.x$sex){添加行(.x,sex='Total',state=first(.x$state),source=first(.x$source),aggregated=1,birtions=sum(.x$birtions))}else.x)
我意识到蒙大拿州需要再增加一行,你可以指定一个
条件,或者你可以使用
附加条件,否则,你可以在后面
用以前的非NAIt填充
。你可以提到我的名字
library(dplyr)
library(tibble)

df %>% 
  group_split(state, source) %>% 
  map_dfr(~ if(all(c('Male', 'Female') %in% .x$sex) && !'Total' %in% .x$sex)  
    { add_row(.x, sex = 'Total', state = first(.x$state), source = first(.x$source), aggregated = 1, births = sum(.x$births)) } 
          else if(all(c('Male', 'Total') %in% .x$sex) && !'Female' %in% .x$sex) 
            { add_row(.x, sex = 'Female', state = first(.x$state), source = first(.x$source), aggregated = 1, births = sum(.x$births)) } 
    else .x)


# A tibble: 11 x 5
   sex    state      source aggregated births
   <chr>  <chr>      <chr>       <dbl>  <dbl>
 1 Male   Montana    UN              0     15
 2 Total  Montana    UN              0     33
 3 Female Montana    UN              1     48
 4 Male   New Jersey WHO             0     20
 5 Female New Jersey WHO             0     30
 6 Total  New Jersey WHO             0     50
 7 Male   Vermont    CDC             0     15
 8 Female Vermont    CDC             0     16
 9 Total  Vermont    CDC             1     31
10 Female Washington CDC             0     27
11 Male   Washington UN              0     20


df %>% 
  group_by(state, source) %>% 
  complete(sex = unique(df$sex)) %>% 
  arrange(state, source, factor(sex, levels = c('Male', 'Female', 'Total'))) %>% 
  filter(sum(is.na(aggregated)) > 1 & !is.na(aggregated)|sum(is.na(aggregated)) <= 1) %>% 
  mutate(aggregated = replace(aggregated, is.na(aggregated), 1), 
         births = case_when(is.na(births) &  row_number() == n() ~ sum(births, na.rm = TRUE), 
                            is.na(births) ~ last(births) - na.omit(births)[1], TRUE ~ births))

# A tibble: 11 x 5
# Groups:   state, source [5]
   state      source sex    aggregated births
   <chr>      <chr>  <chr>       <dbl>  <dbl>
 1 Montana    UN     Male            0     15
 2 Montana    UN     Female          1     18
 3 Montana    UN     Total           0     33
 4 New Jersey WHO    Male            0     20
 5 New Jersey WHO    Female          0     30
 6 New Jersey WHO    Total           0     50
 7 Vermont    CDC    Male            0     15
 8 Vermont    CDC    Female          0     16
 9 Vermont    CDC    Total           1     31
10 Washington CDC    Female          0     27
11 Washington UN     Male            0     20