SKIP TO CONTENT
moonsdata
Darren on the Moon holding a tablet, looking up at a glowing heartbeat line surrounded by data points
[ CASE STUDY / 002 ]
— CASE STUDY / PATIENT SURVIVAL DATA ANALYSIS

9,105 patient records,
cleaned before a single question.

PYTHONPANDASSEABORNEDAGROUP PROJECT2024
MODULEApplied Data Programming
YEAR2024
GRADEHigh 2:1
STACKPython, pandas, seaborn
[ TRANSMISSION 002 ]
— THE BRIEF

Act like a data scientist.
No instructions.

This was a group project for the Applied Data Programming module of my MSc. Four of us were given SUPPORT2, a public dataset of 9,105 critically ill patients from five medical centres in the US, and one instruction. Act as data scientists.

I did the data cleaning, the outlier work and several of the analyses and plots, including deaths by sex, illnesses by ethnicity and the correlation matrix. Then we brought everyone's work together into one notebook.

There was no set question to answer. We had to explore the data ourselves, clean it, look for patterns and explain what we found, all in one Jupyter notebook. Our goal was to understand what affects the survival of critically ill patients. We got a high 2:1.

[ TRANSMISSION 003 ]
— THE CLEANING

Most of the work
was cleaning.

The raw data had 48 columns, and a lot of them were full of gaps. I went through every column with missing values, worked out what it measured, and decided whether to keep it, fill it or drop it.

Income had nearly 3,000 blanks, so I labelled them as undisclosed rather than guess. Lab results with too many missing values were removed, and rows missing key fields like white blood cell count were dropped.

Then I used boxplots to find outliers, like hospital stays over 180 days and temperatures far outside the normal range, and removed the extreme ones.

Dataset cleaning stages
StageRowsColumns
RAW DATASET910548
AFTER MISSING VALUES880224
AFTER OUTLIERS696223
remove_outliers.pyPYTHON
print('There are', df[df['slos'] > 180].shape[0], 'outliers to be removed from the data frame')
print('There are', df[df['d.time'] > 1780].shape[0], 'outliers to be removed from the data frame')

# Deleting all the outliers using the drop() function
df = df.drop(df[df['slos'] > 180].index)
df = df.drop(df[df['d.time'] > 1780].index)
df = df.drop(df[df['charges'] > 400000].index)
df = df.drop(df[df['wblc'] > 24].index)
df = df.drop(df[df['hrt'] > 175].index)
df = df.drop(df[df['temp'] < 34].index)
df = df.drop(df[df['temp'] > 40].index)
df = df.drop(df[df['crea'] > 4].index)

print(f'As you can see the data frame has gone from 8802, to {df.shape[0]}')
Counting the outliers first, then removing them.
[ TRANSMISSION 004 ]
— WHAT WE FOUND

What the data
actually said.

Once the data was clean, I explored it with summary statistics and plots.

  1. 01.

    Around two thirds of the patients, 68% of the 8,802 left after handling missing values, died during follow up.

  2. 02.

    The typical patient was in their 60s. The average age was 62, the median 64 and the most common age 68.

  3. 03.

    Acute respiratory failure and multiple organ failure with sepsis was by far the most common diagnosis, with 2,483 patients. Cirrhosis was the least common, with 405.

  4. 04.

    40% of all the patients were men who died, and 30% were women who died. Men were more likely to die from their diagnosis, and for both men and women, far fewer patients survived than died.

  5. 05.

    The doctors' own survival estimates lined up with what happened. Patients given a better chance of surviving six months were less likely to die.

  6. 06.

    Patients with diabetes tended to have more conditions overall.

Patients per diagnosis (after outlier removal)
ARF/MOSF w/Sepsis 2,483
CHF 1,126
COPD 784
Lung Cancer 758
MOSF w/Malig 536
Coma 437
Colon Cancer 433
Cirrhosis 405
Patients per diagnosis (after outlier removal)
CategoryValueDetails
ARF/MOSF w/Sepsis2,483
CHF1,126
COPD784
Lung Cancer758
MOSF w/Malig536
Coma437
Colon Cancer433
Cirrhosis405
Share of each sex who died (after outlier removal)
Men 71.5% died (2,812 of 3,933)
Women 68.3% died (2,069 of 3,029)

Out of all patients, 40% were men who died and 30% were women who died. Within each sex, the death rates are much closer.

Share of each sex who died (after outlier removal)
CategoryValueDetails
Men71.5%2,812 of 3,933
Women68.3%2,069 of 3,029

Rebuilt from our cleaned dataset of 6,962 patients.

[ TRANSMISSION 005 ]
— LOOKING BACK

What I'd do
differently now.

I really enjoyed this project. Looking at it again now, after working on real client data at Squarcle, there are a few things I'd do differently.

  1. 01.

    Be careful what you call an outlier. We removed patients followed up for more than 1,780 days, but 362 of those 372 were still alive. They weren't errors, they were the survivors, and removing them made the death rate look higher than it really was.

  2. 02.

    Don't fill money with zero. Filling missing hospital charges with 0 made some stays look free. Leaving them blank, or using the median, would have been more honest.

  3. 03.

    Go beyond describing the data. Now I'd build a machine learning model, for example to predict hospital charges or which patients are most at risk, and test how well it works on patients it hasn't seen.

  4. 04.

    Write less code, and reuse more of it. My notebook repeats the same plotting and filtering code again and again. After Squarcle, I'd wrap those steps into small reusable functions, so the notebook is shorter and easier to check.

  5. 05.

    Give the notebook a clearer structure. Each section should lead into the next, so anyone reading can follow the story from raw data to findings without getting lost.

  6. 06.

    Take the visuals further. Seaborn did the job, but now I'd build the final findings in Power BI, so the people reading them could filter and explore the data themselves.

— END OF TRANSMISSION 005