# Loading the library
library(tidyverse)Who Spends the Most Time in Space?
Introduction
For this project I am using the Astronauts dataset from the CORGIS Dataset Project. It contains publicly available information about every astronaut who flew on a space mission before January 15, 2020. The data was originally collected by Mariya Stavnichuk and Tatsuya Corlett from NASA, Roscosmos, and other official mission records, and was later shared through the TidyTuesday project.
Each row in the dataset is one astronaut on one mission, so astronauts who flew several times appear in several rows. The dataset has 1,277 rows and 20 variables. Some of the variables I will use are:
- Name, Gender, and Nationality (categorical)
- Birth Year and Selection Year (the year they were chosen to be an astronaut)
- Mission Year (the year of each mission)
- Lifetime Mission Count (how many missions the astronaut flew in total)
- Lifetime Mission Duration (total hours spent in space over their whole career)
My main question is: What explains how much total time an astronaut spends in space? I expected the number of missions to be the biggest factor, but I also want to see whether the astronaut’s country and the year they were selected matter.
I chose this topic because space travel has changed a lot since 1961. Early missions lasted a few hours, while today astronauts can live on the International Space Station for six months or more.
Load the Libraries and Data
# Reading the dataset file
astronauts <- read_csv("../datasets/astronauts.csv")Exploring the Data
str(astronauts)spc_tbl_ [1,270 × 20] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ Profile.Astronaut Numbers.Overall : num [1:1270] 1 2 3 3 4 5 5 6 6 7 ...
$ Profile.Astronaut Numbers.Nationwide : num [1:1270] 1 2 1 1 2 2 2 4 4 3 ...
$ Profile.Name : chr [1:1270] "Gagarin, Yuri" "Titov, Gherman" "Glenn, John H., Jr." "Glenn, John H., Jr." ...
$ Profile.Gender : chr [1:1270] "male" "male" "male" "male" ...
$ Profile.Birth Year : num [1:1270] 1934 1935 1921 1921 1925 ...
$ Profile.Nationality : chr [1:1270] "U.S.S.R/Russia" "U.S.S.R/Russia" "U.S." "U.S." ...
$ Profile.Military : logi [1:1270] TRUE TRUE TRUE TRUE TRUE TRUE ...
$ Profile.Selection.Group : chr [1:1270] "TsPK-1" "TsPK-1" "NASA Astronaut Group 1" "NASA Astronaut Group 2" ...
$ Profile.Selection.Year : num [1:1270] 1960 1960 1959 1959 1959 ...
$ Profile.Lifetime Statistics.Mission count : num [1:1270] 1 1 2 2 1 2 2 2 2 3 ...
$ Mission.Role : chr [1:1270] "pilot" "pilot" "pilot" "PSP" ...
$ Mission.Year : num [1:1270] 1961 1961 1962 1998 1962 ...
$ Mission.Name : chr [1:1270] "Vostok 1" "Vostok 2" "MA-6" "STS-95" ...
$ Mission.Vechicles.Ascent : chr [1:1270] "Vostok 1" "Vostok 2" "MA-6" "STS-95" ...
$ Mission.Vechicles.Orbit : chr [1:1270] "Vostok 2" "Vostok 2" "MA-6" "STS-95" ...
$ Mission.Vechicles.Decent : chr [1:1270] "Vostok 3" "Vostok 2" "MA-6" "STS-95" ...
$ Mission.Durations.Mission duration : num [1:1270] 1.77 25 5 213 5 ...
$ Profile.Lifetime Statistics.Mission duration: num [1:1270] 1.77 25.3 218 218 5 ...
$ Mission.Durations.EVA duration : num [1:1270] 0 0 0 0 0 0 0 0 0 0 ...
$ Profile.Lifetime Statistics.EVA duration : num [1:1270] 0 0 0 0 0 0 0 0 0 0 ...
- attr(*, "spec")=
.. cols(
.. `Profile.Astronaut Numbers.Overall` = col_double(),
.. `Profile.Astronaut Numbers.Nationwide` = col_double(),
.. Profile.Name = col_character(),
.. Profile.Gender = col_character(),
.. `Profile.Birth Year` = col_double(),
.. Profile.Nationality = col_character(),
.. Profile.Military = col_logical(),
.. Profile.Selection.Group = col_character(),
.. Profile.Selection.Year = col_double(),
.. `Profile.Lifetime Statistics.Mission count` = col_double(),
.. Mission.Role = col_character(),
.. Mission.Year = col_double(),
.. Mission.Name = col_character(),
.. Mission.Vechicles.Ascent = col_character(),
.. Mission.Vechicles.Orbit = col_character(),
.. Mission.Vechicles.Decent = col_character(),
.. `Mission.Durations.Mission duration` = col_double(),
.. `Profile.Lifetime Statistics.Mission duration` = col_double(),
.. `Mission.Durations.EVA duration` = col_double(),
.. `Profile.Lifetime Statistics.EVA duration` = col_double()
.. )
- attr(*, "problems")=<pointer: 0x000002299bcca100>
# Checking for missing values in each column
colSums(is.na(astronauts)) Profile.Astronaut Numbers.Overall
0
Profile.Astronaut Numbers.Nationwide
0
Profile.Name
0
Profile.Gender
0
Profile.Birth Year
0
Profile.Nationality
0
Profile.Military
0
Profile.Selection.Group
0
Profile.Selection.Year
0
Profile.Lifetime Statistics.Mission count
0
Mission.Role
0
Mission.Year
0
Mission.Name
0
Mission.Vechicles.Ascent
0
Mission.Vechicles.Orbit
0
Mission.Vechicles.Decent
0
Mission.Durations.Mission duration
0
Profile.Lifetime Statistics.Mission duration
0
Mission.Durations.EVA duration
0
Profile.Lifetime Statistics.EVA duration
0
Clean the Data
The column names are long and have dots and spaces in them, so I will rename the columns I need and keep only those.
# Renaming long column names to short and keeping only the columns needed
missions <- astronauts |>
rename(name = `Profile.Name`,
gender = `Profile.Gender`,
birth_year = `Profile.Birth Year`,
nationality = `Profile.Nationality`,
selection_year = `Profile.Selection.Year`,
mission_year = `Mission.Year`,
mission_count = `Profile.Lifetime Statistics.Mission count`,
lifetime_hours = `Profile.Lifetime Statistics.Mission duration`) |>
select(name, gender, birth_year, nationality, selection_year,
mission_year, mission_count, lifetime_hours)
head(missions)# A tibble: 6 × 8
name gender birth_year nationality selection_year mission_year mission_count
<chr> <chr> <dbl> <chr> <dbl> <dbl> <dbl>
1 Gagar… male 1934 U.S.S.R/Ru… 1960 1961 1
2 Titov… male 1935 U.S.S.R/Ru… 1960 1961 1
3 Glenn… male 1921 U.S. 1959 1962 2
4 Glenn… male 1921 U.S. 1959 1998 2
5 Carpe… male 1925 U.S. 1959 1962 1
6 Nikol… male 1929 U.S.S.R/Ru… 1960 1962 2
# ℹ 1 more variable: lifetime_hours <dbl>
Next I create three new variables with mutate():
- lifetime_days: hours are hard to picture, so I divide by 24 to get days.
- age_at_mission: mission year minus birth year.
- country_group: the U.S. and Russia (including the former Soviet Union) have flown most of the astronauts, so I group every other country together as “Other”.
# Createing new variables:
# lifetime_days - total days in space
# age_at_mission - age of the astronaut at each mission
# country_group - country group (U.S., Russia, Other)
missions <- missions |>
mutate(lifetime_days = lifetime_hours / 24,
age_at_mission = mission_year - birth_year,
country_group = ifelse(nationality == "U.S.", "U.S.",
ifelse(nationality == "U.S.S.R/Russia", "Russia", "Other")),
country_group = factor(country_group, levels = c("U.S.", "Russia", "Other")))Because each row is one mission, astronauts with many missions appear many times. For questions about an astronaut’s whole career, I need one row per astronaut, so I use distinct() to keep only the first row for each name.
# Keeping one row for each astronaut and counting them
astronaut_totals <- missions |>
distinct(name, .keep_all = TRUE)
nrow(astronaut_totals)[1] 559
This leaves 564 unique astronauts.
# Quick summaries (gender, countries, days in space)
table(astronaut_totals$gender)
female male
63 496
table(astronaut_totals$country_group)
U.S. Russia Other
341 122 96
summary(astronaut_totals$lifetime_days) Min. 1st Qu. Median Mean 3rd Qu. Max.
0.02542 12.54167 28.00000 100.14560 156.18750 878.48000
Out of 564 astronauts, 500 are men and 64 are women. 344 are from the U.S., 122 are from Russia/USSR, and 98 are from other countries. The median astronaut spent about 28 days in space over their career, but the mean is about 99 days. That big difference tells me the data is right-skewed: a small number of astronauts spent a very long time in space.
Simple Linear Regression
First I check whether the number of missions predicts total days in space.
# Simple linear regression: does the number of missions predict total days in space?
model1 <- lm(lifetime_days ~ mission_count, data = astronaut_totals)
summary(model1)
Call:
lm(formula = lifetime_days ~ mission_count, data = astronaut_totals)
Residuals:
Min 1Q Median 3Q Max
-198.97 -82.08 -50.92 55.43 687.82
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 24.77 12.59 1.967 0.0497 *
mission_count 33.18 4.83 6.869 1.73e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 146.1 on 557 degrees of freedom
Multiple R-squared: 0.0781, Adjusted R-squared: 0.07644
F-statistic: 47.18 on 1 and 557 DF, p-value: 1.733e-11
Equation: lifetime_days = 23.68 + 33.46 × mission_count
Interpretation: For each additional mission, an astronaut spends about 33 more days in space on average. The p-value for mission count is very small (less than 0.001), so the relationship is statistically significant. However, the adjusted R² is only about 0.08, which means the number of missions explains only about 8% of the variation in total time in space. That surprised me. Some astronauts flew five or six short Space Shuttle missions, while others flew only two long missions on a space station, so mission count alone is not a great predictor.
Multiple Linear Regression
Next I add selection year and country group to the model.
# Multiple linear regression: adding selection year and country group to predict total days in space
model2 <- lm(lifetime_days ~ mission_count + selection_year + country_group,
data = astronaut_totals)
summary(model2)
Call:
lm(formula = lifetime_days ~ mission_count + selection_year +
country_group, data = astronaut_totals)
Residuals:
Min 1Q Median 3Q Max
-278.85 -75.95 -19.22 59.81 541.97
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -8673.6227 732.2468 -11.845 <2e-16 ***
mission_count 38.0805 3.8974 9.771 <2e-16 ***
selection_year 4.3504 0.3686 11.803 <2e-16 ***
country_groupRussia 208.3705 12.1883 17.096 <2e-16 ***
country_groupOther 11.3843 13.8856 0.820 0.413
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 113.3 on 554 degrees of freedom
Multiple R-squared: 0.4484, Adjusted R-squared: 0.4444
F-statistic: 112.6 on 4 and 554 DF, p-value: < 2.2e-16
Equation: lifetime_days = −8539.42 + 38.43 × mission_count + 4.28 × selection_year + 208.79 × (Russia) + 12.27 × (Other)
(U.S. is the reference group, so “Russia” and “Other” are compared to U.S. astronauts.)
Interpretation:
- Mission count is still significant (p < 0.001). Holding the other variables constant, each extra mission adds about 38 days.
- Selection year is significant (p < 0.001). Astronauts selected one year later spend about 4.3 more days in space, likely because newer astronauts fly long missions to the International Space Station.
- Russia is significant (p < 0.001). Russian cosmonauts spend about 209 more days in space than U.S. astronauts with the same number of missions and selection year.
- Other countries are not significant (p ≈ 0.37), so they are not very different from U.S. astronauts.
- The intercept (−8539) does not have a real-world meaning because nobody was selected in year 0. It just positions the line.
The adjusted R² jumped from about 0.08 to about 0.44. This model explains about 44% of the variation in total time in space, which is much better than the first model. Country was the missing piece.
Diagnostic Plots
# Showing the four diagnostic plots for 2x2 grid to check the residuals
par(mfrow = c(2, 2))
plot(model2)The residuals are not perfectly spread out. The points fan out for larger predicted values, and the Q-Q plot curves up at the right end, which matches what I saw earlier: the data is right-skewed because a few astronauts spent hundreds of days in space. So the model is useful, but its predictions are less reliable for astronauts with very long careers.
Visualizations
Plot 1: Missions vs. Total Days in Space
# Scatterplot of missions and total days in space
ggplot(astronaut_totals, aes(x = mission_count, y = lifetime_days, color = country_group)) +
geom_jitter(width = 0.15, alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE) +
scale_color_manual(values = c("U.S." = "#1f4e9c", "Russia" = "#c0392b", "Other" = "#f39c12")) +
labs(title = "More Missions Means More Time in Space, Especially for Russia",
x = "Lifetime Number of Missions",
y = "Total Days in Space",
color = "Country Group",
caption = "Source: CORGIS Astronauts Dataset") +
theme_minimal()The Russian line is much steeper than the others. I used geom_jitter() because mission count is a whole number, so many points would otherwise sit on top of each other.
Plot 2: Total Days in Space by Country Group
# Boxplot which shows comparison of total days in space by country group
ggplot(astronaut_totals, aes(x = country_group, y = lifetime_days, fill = country_group)) +
geom_boxplot() +
scale_fill_manual(values = c("U.S." = "#1f4e9c", "Russia" = "#c0392b", "Other" = "#f39c12")) +
labs(title = "Russian Cosmonauts Spend Far More Time in Space",
x = "Country Group",
y = "Total Days in Space",
caption = "Source: CORGIS Astronauts Dataset") +
theme_minimal() +
theme(legend.position = "none")The median Russian cosmonaut spent about 185 days in space, compared to about 27 days for U.S. astronauts and 16 days for other countries.
Plot 3: Average Age at Mission by Decade
For this plot I use the missions data (one row per mission), since age is different for each mission.
# Groupping missions by decade and calculating the average astronaut age for each decade
age_by_decade <- missions |>
mutate(decade = floor(mission_year / 10) * 10) |>
group_by(decade) |>
summarize(avg_age = mean(age_at_mission))
# With this line chart I'm showing how the average age at mission changed over the decades
ggplot(age_by_decade, aes(x = decade, y = avg_age)) +
geom_line(color = "#1f4e9c", linewidth = 1) +
geom_point(color = "#c0392b", size = 3) +
labs(title = "Astronauts Are Getting Older",
x = "Decade",
y = "Average Age at Mission",
caption = "Source: CORGIS Astronauts Dataset") +
theme_minimal()The average age at mission rose from about 36 in the 1960s to about 46 in the 2010s.
Plot 4: Astronauts by Gender
# Bar chart showing the number of astronauts by gender
ggplot(astronaut_totals, aes(x = gender, fill = gender)) +
geom_bar() +
scale_fill_manual(values = c("female" = "#8e44ad", "male" = "#16a085")) +
labs(title = "Most Astronauts Have Been Men",
x = "Gender",
y = "Number of Astronauts",
caption = "Source: CORGIS Astronauts Dataset") +
theme_minimal() +
theme(legend.position = "none")Conclusion
The data needed some cleaning before I could analyze it. The column names were long and hard to type, so I renamed the ones I used. The biggest cleaning issue was that each row is a mission, not an astronaut. If I had run the regression on all 1,277 rows, astronauts with many missions would have been counted several times, so I used distinct() to keep one row per astronaut. I also converted hours into days and created a simpler country group variable.
The most surprising result was how weak the first model was. I expected the number of missions to explain most of the time spent in space, but it explained only about 8%. Once I added country group and selection year, the model explained about 44%. Russian cosmonauts spend around 209 more days in space than U.S. astronauts with the same number of missions. This makes sense historically: the Soviet Union and Russia focused on long stays on space stations like Salyut, Mir, and later the ISS, while the U.S. flew many short Space Shuttle missions. The record for the longest single mission in the dataset belongs to Valeri Polyakov, who spent about 438 days in space on one trip.
The visualizations also showed that astronauts have gotten older over time and that most astronauts have been men, although the share of women has grown since the 1980s.
If I had more time, I would look at the data mission by mission and see how mission length changed over the decades, especially after the ISS opened in 2000. I would also like to try a log transformation of total days, since the data is right-skewed, to see if it improves the diagnostic plots.
References
- CORGIS Datasets Project. Astronauts CSV File. https://corgis-edu.github.io/corgis/csv/astronauts/