R: Changing Duplicate Values Within Subjects

Viewed 742
 Subj  Trial  Time
1 A      1     250
2 A      2     250
3 A      3     280
4 B      1     250
5 B      2     270
6 B      3     290

Above is an example of the data I am working with. I have different subjects (Subj) performing the same set of trials (Trial). Unfortunately, when trial event happen in rapid succession, my equipment will print the same time values (see Time column for rows 1 and 2).

I cannot have the same subject have duplicate time values, HOWEVER, it is okay for different subjects to have the same time values. Thus, I need a way to conditionally change duplicates such that ONLY time duplicates within a particular subject are changed.

Ideally, I'd like to change the above example to something like below:

 Subj  Trial  Time
1 A      1     250
2 A      2     250.5
3 A      3     280
4 B      1     250
5 B      2     270
6 B      3     290

Any recommendations on how I might be able to accomplish this?

Thanks!

3 Answers

A solution using dplyr. We can group the data by Subj and Time, count the number of occurrences, and then change the Time by adding 0.5 if number of occurrences is more than 1.

library(dplyr)

dt2 <- dt %>%
  group_by(Subj, Time) %>%
  mutate(Count = row_number()) %>%
  ungroup() %>%
  mutate(Time = ifelse(Count > 1, Time + 0.5, Time)) %>%
  select(-Count)
dt2
# # A tibble: 6 x 3
#    Subj Trial  Time
#   <chr> <int> <dbl>
# 1     A     1 250.0
# 2     A     2 250.5
# 3     A     3 280.0
# 4     B     1 250.0
# 5     B     2 270.0
# 6     B     3 290.0

DATA

dt <- read.table(text = " Subj  Trial  Time
1 A      1     250
2 A      2     250
3 A      3     280
4 B      1     250
5 B      2     270
6 B      3     290",
                 header = TRUE, stringsAsFactors = FALSE)

Somewhat similar to the already provided solution, but without counting. This is comprised of two solutions:

base R:

do.call(rbind, lapply(split(df, list(df$Subj, df$Time)), function(x) {
    x$Time <- x$Time + seq(0, by=0.5, length.out=nrow(x))
    x
}))

tidyverse

library(dplyr)


df %>%
    group_by(Subj, Time) %>%
    mutate(Time2 = Time + seq(0, by=0.5, length.out=n()))

They should both yield something similar to the following:

#  Subj Trial Time 
#  A    1     250.0
#  A    2     250.5
#  B    1     250.0
#  B    2     270.0
#  A    3     280.0
#  B    3     290.0

The key is to split your dataframe into chunks defined by the combination of the columns of Subj and Time. From here, the remaining is easy: you increment the values in the Time column by 0.5 starting with 0, with the length of such a sequence being the same as that of the chunk.

I hope this proves useful.

Here is a base R option with duplicated. We create a logical index of duplicate elements based on the columns 'Subj', 'Time' and then assign the 'Time' value for those elements by adding 0.5 to it.

i1 <- duplicated(df1[c('Subj', 'Time')])
df1$Time[i1] <- df1$Time[i1] + 0.5
df1
#  Subj Trial  Time
#1    A     1 250.0
#2    A     2 250.5
#3    A     3 280.0
#4    B     1 250.0
#5    B     2 270.0
#6    B     3 290.0
Related