I think most articles on "censored variables" will be related to the response variable which is quite a different story.
Being a censored regressor is not automatically a problem. If you are not fully trusting this regressor or if the corresponding "residuals versus variable"-plot shows troubles in the two extreme values 21 and 60, then you can still decide to add dummy variables like
year60
: 1 if 60 or above, 0 otherwise
year21
: 1 if 21 or below, 0 otherwise
to the regression to allow the model to be flexible enough to represent the relationship.
Of course, because you don't have values outside the interval from 21 to 60, nothing can be made to recover the information loss. All you can do is trying to choose a flexibly enough regression equation.
Let me demonstrate the idea on a simple example with just this one covariable in R
# Step 1: Generate and visualize data
set.seed(29)
age <- 15:90
ageCensored <- pmin(60, pmax(21, age)) # censored at 21 an 60
outcome <- 20 + 0.5 * age + 0.03 * (age - 40)^2 + rnorm(length(age))*10
plot(outcome ~ ageCensored)
# Simple linear regression, ignoring for potential misfit at the endpoints
fit <- lm(outcome ~ ageCensored)
summary(fit)
abline(fit, col = "red") # to add the regression line to the scatter plot above
# Output
Estimate Std. Error t value Pr(>|t|)
(Intercept) 17.30597 3.99649 4.330 4.61e-05 ***
ageCensored 0.60062 0.08176 7.346 2.21e-10 ***
[...]
Residual standard error: 10.39 on 74 degrees of freedom
Multiple R-squared: 0.4217, Adjusted R-squared: 0.4139
F-statistic: 53.97 on 1 and 74 DF, p-value: 2.213e-10
# Residual versus fitted plot shows considerable misfit which is also directly visible from the scatter plot with the regression line
plot(fit, which = 1)
# Now we can either improve the fit by using a squared age effect (by knowing how the data way generated) or using the dummy "trick" mentioned above. Let's try with the dummy trick.
fit2 <- lm(outcome ~ ageCensored + I(ageCensored == 21) + I(ageCensored == 60))
summary(fit2)
plot(fit2, which = 1)
# Results
Estimate Std. Error t value Pr(>|t|)
(Intercept) 31.0754 10.4810 2.965 0.0041 **
ageCensored 0.3242 0.2498 1.298 0.1984
I(ageCensored == 21)TRUE 4.3685 8.4830 0.515 0.6082
I(ageCensored == 60)TRUE 43.7598 6.3583 6.882 1.82e-09 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 16.89 on 72 degrees of freedom
Multiple R-squared: 0.6965, Adjusted R-squared: 0.6838
F-statistic: 55.07 on 3 and 72 DF, p-value: < 2.2e-16
# Residuals versus fitted plot looks better now (although heterogeneity can be spottet at the right endpoint, a problem which I do not account for simplicity)
# Plot of the regression function against age
plot(outcome ~ ageCensored, xlim = range(age), xlab = "age")
lines(age, predict(fit2, list(ageCensored)), col = "red")
Note that since in your data, you cannot distinguish a 60 year old person with a person older than 60 (i.e. you don't know what value is really censored), you cannot do much more here. If you had this information, you could slighly redefine the dummy variables to
year>60
: 1 if above 61, 0 otherwise
year<21
: 1 if below 21, 0 otherwise
to treat persons ages 60 or 21 separately from the censored ones.