How about this (using base R):
dt$size="small" a=aggregate(dt$thread~dt$diameter, dt, max)[,"dt$thread"] dt[dt$thread %in% a,]$size="large"
OUTPUT
diameter thread size 1 1 4 small 2 1 6 large 3 1 4 small 4 2 5 small 5 2 7 large 6 3 9 large
DATA
dt=structure(list(diameter = c(1L, 1L, 1L, 2L, 2L, 3L), thread = c(4L, 6L, 4L, 5L, 7L, 9L)), .Names = c("diameter", "thread"), class = "data.frame", row.names = c(NA, -6L))
REFERENCE
library(dplyr) library(microbenchmark) dt=structure(list(diameter = c(1L, 1L, 1L, 2L, 2L, 3L), thread = c(4L, 6L, 4L, 5L, 7L, 9L)), .Names = c("diameter", "thread"), class = "data.frame", row.names = c(NA, -6L)) func_ZachTurn <- function(data){data %>% group_by(diameter) %>% mutate(size=ifelse(thread==max(thread),"large","small"))} func_m0h3n <- function(dt){dt$size="small";a=aggregate(dt$thread~dt$diameter, dt, max)[,"dt$thread"];dt[dt$thread %in% a,]$size="large";dt} func_Psidom <- function(df){data.table::setDT(df);df[, size := c("small", "large")[(thread == max(thread)) + 1L], .(diameter)];df[];} f <- function(x) (if(length(x)==1) 1L else x == max(x)) + 1L func_docendo.discimus <- function(dat){dat$size <- c("small", "large")[ave(dat$thread, dat$diameter, FUN = f)];dat;} func_Ernest.A <- function(df){df$size <- factor(unsplit(lapply(split(df$thread, df$diameter), function(x) ifelse(x == max(x), 'large', 'small')), df$diameter));df;} r <- func_ZachTurn(dt) all(r == func_m0h3n(dt))