parse values based on groups in R

五迷三道 提交于 2019-12-12 12:16:38

问题


I have a very large dataset and a sample of that looks something like the one below:

| Id | Name    | Start_Date | End_Date   |
|----|---------|------------|------------|
| 10 | Mark    | 4/2/1999   | 7/5/2018   |
| 10 |         | 1/1/2000   | 9/24/2018  |
| 25 |         | 5/3/1968   | 6/3/2000   |
| 25 |         | 6/6/2009   | 4/23/2010  |
| 25 | Anthony | 2/20/2010  | 7/21/2016  |
| 25 |         | 9/12/2014  | 11/26/2019 |

I need to parse the names from Name column based on their Id such that the output table looks like:

| Id | Name    | Start_Date | End_Date   |
|----|---------|------------|------------|
| 10 | Mark    | 4/2/1999   | 7/5/2018   |
| 10 | Mark    | 1/1/2000   | 9/24/2018  |
| 25 | Anthony | 5/3/1968   | 6/3/2000   |
| 25 | Antony  | 6/6/2009   | 4/23/2010  |
| 25 | Anthony | 2/20/2010  | 7/21/2016  |
| 25 | Anthony | 9/12/2014  | 11/26/2019 |

How can I achieve an output as shown above? I went through the substitute and parse functions, but was unable to understand how they apply to this problem.

My dataset would be:

df=data.frame(Id=c("10","10","25","25","25","25"),Name=c("Mark","","","","Anthony",""),
              Start_Date=c("4/2/1999", "1/1/2000","5/3/1968","6/6/2009","2/20/2010","9/12/2014"),
              End_Date=c("7/5/2018","9/24/2018","6/3/2000","4/23/2010","7/21/2016","11/26/2019"))

回答1:


We can change the blanks ("") to NA and use fill to replace the NA elements with the previous non-NA element

library(dplyr)
library(tidyr)
df1 %>%      
   mutate(Name = na_if(Name, "")) %>%
   group_by(Id) %>%
   fill(Name, .direction = "down") %>%
   fill(Name, .direction = "up)
# A tibble: 6 x 4
# Groups:   Id [2]
#  Id    Name    Start_Date End_Date  
#  <chr> <chr>   <chr>      <chr>     
#1 10    Mark    4/2/1999   7/5/2018  
#2 10    Mark    1/1/2000   9/24/2018 
#3 25    Anthony 5/3/1968   6/3/2000  
#4 25    Anthony 6/6/2009   4/23/2010 
#5 25    Anthony 2/20/2010  7/21/2016 
#6 25    Anthony 9/12/2014  11/26/2019

In the devel version of tidyr (‘0.8.3.9000’), this can be done in a single fill statement as .direction = "downup" is also an option

df1 %>%      
   mutate(Name = na_if(Name, "")) %>%
   group_by(Id) %>%
   fill(Name, .direction = "downup") 

Or another option is to group by 'Id', and mutate the 'Name' as the first non-blank element

df1 %>%
    group_by(Id) %>%        
    mutate(Name = first(Name[Name!=""])) 
# A tibble: 6 x 4
# Groups:   Id [2]
#  Id    Name    Start_Date End_Date  
#  <chr> <chr>   <chr>      <chr>     
#1 10    Mark    4/2/1999   7/5/2018  
#2 10    Mark    1/1/2000   9/24/2018 
#3 25    Anthony 5/3/1968   6/3/2000  
#4 25    Anthony 6/6/2009   4/23/2010 
#5 25    Anthony 2/20/2010  7/21/2016 
#6 25    Anthony 9/12/2014  11/26/2019

data

df1 <- structure(list(Id = c("10", "10", "25", "25", "25", "25"), Name = c("Mark", 
"", "", "", "Anthony", ""), Start_Date = c("4/2/1999", "1/1/2000", 
"5/3/1968", "6/6/2009", "2/20/2010", "9/12/2014"), End_Date = c("7/5/2018", 
"9/24/2018", "6/3/2000", "4/23/2010", "7/21/2016", "11/26/2019"
)), class = "data.frame", row.names = c(NA, -6L))



回答2:


Using DF defined reproducibly in the Note at the end, replace each zero-length element of Name with NA and then use na.omit to get the unique non-NA to use to fill. We have assumed that there is only one non-NA per Id which is the case in the question. If not we could replace na.omit with function(x) unique(na.omit(x)) assuming that the non-NAs are all the same within Id. No packages are used.

transform(DF, Name = ave(replace(Name, !nzchar(Name), NA), Id, FUN = na.omit))

giving:

  Id    Name Start_Date   End_Date
1 10    Mark   4/2/1999   7/5/2018
2 10    Mark   1/1/2000  9/24/2018
3 25 Anthony   5/3/1968   6/3/2000
4 25 Anthony   6/6/2009  4/23/2010
5 25 Anthony  2/20/2010  7/21/2016
6 25 Anthony  9/12/2014 11/26/2019

na.strings

We can simplify this slightly if we make sure that the zero length elements of Name are NA in the first place. We replace the read.table line in the Note with the first line below. Then it is just a matter of using na.locf0.

DF <- read.table(text = Lines, header = TRUE, as.is = TRUE, sep = "|", 
  strip.white = TRUE, na.strings = "")

transform(DF, Name = ave(Name, Id, FUN = na.omit))

Note

The input in reproducible form:

Lines <- "
 Id | Name    | Start_Date | End_Date   
 10 | Mark    | 4/2/1999   | 7/5/2018   
 10 |         | 1/1/2000   | 9/24/2018  
 25 |         | 5/3/1968   | 6/3/2000   
 25 |         | 6/6/2009   | 4/23/2010  
 25 | Anthony | 2/20/2010  | 7/21/2016  
 25 |         | 9/12/2014  | 11/26/2019"
DF <- read.table(text = Lines, header = TRUE, as.is = TRUE, sep = "|", strip.white = TRUE)


来源:https://stackoverflow.com/questions/57677225/parse-values-based-on-groups-in-r

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!