In [1]:
%matplotlib inline
import math
import pandas as pd
import matplotlib.pyplot as plt
from collections import Counter
from functools import reduce
In [2]:
df = pd.read_csv('data/bc.data', names=[
'age',
'menopause',
'tumor-size',
'inv-nodes',
'node-caps',
'deg-malig',
'breast',
'breast-quad',
'irradiat',
'class'
])
# слагане на нули пред единичните цифри, за да има е валидна лексикографската сортировка на данните
df['tumor-size'] = df['tumor-size'].replace(' 5-9', ' 05-09').replace(' 0-4', ' 00-04')
df.head()
Out[2]:
In [3]:
# interval_string is a string with the format 'a-b', e.g. '50-59'
def get_interval_mean(interval_string):
a, b = list(map(float, interval_string.split('-')))
return (a + b) / 2 # TODO: overlapping intervals?
def get_binned_mean(series):
counts = series.value_counts().to_dict().items()
sum = reduce(lambda acc, x: acc + get_interval_mean(x[0]) * x[1], counts, 0)
return sum / series.size
In [4]:
def approximate_quantile(series, quantile = 0.5):
sorted_series = series.sort_values().reset_index(drop=True)
N = math.floor((series.size + 1) * quantile) - 1 # index from zero
cumsum = series.value_counts().sort_index().cumsum()
group_key = sorted_series[N]
group_index = cumsum.index.get_loc(group_key)
previous_group_index = group_index - 1 # TODO: clamp to zero?
previous_cumsum = cumsum[cumsum.index[previous_group_index]]
L, H = list(map(float, group_key.split('-')))
group_size = cumsum[group_key] - previous_cumsum
return L + (N - previous_cumsum) * ((H - L) / group_size)
In [5]:
def approximate_mode(series):
value_counts = series.value_counts().sort_index()
mode_key = value_counts.sort_values(ascending=False).index[0]
L, H = list(map(float, mode_key.split('-')))
mode_index = value_counts.index.get_loc(mode_key)
cur_count = value_counts[mode_index]
prev_count = value_counts[mode_index - 1]
next_count = value_counts[mode_index + 1]
return L + ((cur_count - prev_count) * (H - L)) / (2 * cur_count - prev_count - next_count)
In [6]:
# помощна функция за изчисляване на всички статистики за първите 6 задачи
def get_stats(series):
mean = get_binned_mean(series)
mode = approximate_mode(series)
median = approximate_quantile(series)
q1 = approximate_quantile(series, quantile=1/4)
q3 = approximate_quantile(series, quantile=3/4)
iqr = q3 - q1
lower_fence = q1 - 1.5 * iqr
upper_fence = q3 + 1.5 * iqr
return {
'mean': mean,
'mode': mode,
'median': median,
'q1': q1,
'q3': q3,
'xmin': max(0, lower_fence), # age and tumor size cannot be negative
'xmax': upper_fence,
}
In [7]:
# функции за чертаене на boxplot
def get_boxplot_stats(stats, label):
return {
'med': stats['median'],
'q1': stats['q1'],
'q3': stats['q3'],
'whislo': stats['xmin'],
'whishi': stats['xmax'],
'label': label
}
def draw_boxplot(stats, label):
boxplot_stats = get_boxplot_stats(stats, label)
fig, ax = plt.subplots(1, 1)
fig.set_size_inches(5, 6)
ax.bxp([boxplot_stats], showfliers=False)
In [8]:
def print_summary(stats):
print('Средно аритметично: {}'.format(stats['mean']))
print('Медиана: {}'.format(stats['median']))
print('Мода: {}'.format(stats['mode']))
print()
print('Xmin: {}'.format(stats['xmin']))
print('Q1: {}'.format(stats['q1']))
print('Q2 (Медиана): {}'.format(stats['median']))
print('Q3: {}'.format(stats['q3']))
print('Xmax: {}'.format(stats['xmax']))
In [9]:
stats_age = get_stats(df.age)
print_summary(stats_age)
In [10]:
draw_boxplot(stats_age, label='Age')
In [11]:
df_age_recurrence = df[df['class'] == ' recurrence-events'].age
stats_age_recurrence = get_stats(df_age_recurrence)
print_summary(stats_age_recurrence)
In [12]:
draw_boxplot(stats_age_recurrence, label='Age Recurrence')
In [13]:
df_age_no_recurrence = df[df['class'] == ' no-recurrence-events'].age
stats_age_no_recurrence = get_stats(df_age_no_recurrence)
print_summary(stats_age_no_recurrence)
In [14]:
draw_boxplot(stats_age_no_recurrence, label='Age No Recurrence')
In [15]:
fig, ax = plt.subplots(1, 1)
fig.set_size_inches(10, 12)
plot = ax.bxp([
get_boxplot_stats(stats_age, 'Age'),
get_boxplot_stats(stats_age_recurrence, 'Age Recurrence'),
get_boxplot_stats(stats_age_no_recurrence, 'Age No Recurrence')
], showfliers=False)
От дадените boxplot графики не се забелязва драстична разлика между трите групи данни.
Това, което се забелязва, е че интервалът на възраст на жени, при които се забелязва повтаряне на рака на гърдата след проведената операция, е по-широк от този интервал на жени, при които НЕ се забелязва повтаряне на рака на гърдата след операция.
Освен това, модата при средната група (от трите разгледани) е 47.71, докато при другите две групи е 51.2 и 51.85, които са по-близо до медианата и средно-аритметичната стойност.
In [16]:
stats_tumor = get_stats(df['tumor-size'])
print_summary(stats_tumor)
In [17]:
draw_boxplot(stats_tumor, 'Tumor Size')
In [18]:
df_tumor_recurrence = df[df['class'] == ' recurrence-events']['tumor-size']
stats_tumor_recurrence = get_stats(df_tumor_recurrence)
print_summary(stats_tumor_recurrence)
In [19]:
draw_boxplot(stats_tumor_recurrence, label='Tumor Size Recurrence')
In [20]:
df_tumor_no_recurrence = df[df['class'] == ' no-recurrence-events']['tumor-size']
stats_tumor_no_recurrence = get_stats(df_tumor_no_recurrence)
print_summary(stats_tumor_no_recurrence)
In [21]:
draw_boxplot(stats_tumor_no_recurrence, label='Tumor Size No Recurrence')
In [22]:
fig, ax = plt.subplots(1, 1)
fig.set_size_inches(10, 12)
plot = ax.bxp([
get_boxplot_stats(stats_tumor, 'Tumor Size'),
get_boxplot_stats(stats_tumor_recurrence, 'Tumor Size Recurrence'),
get_boxplot_stats(stats_tumor_no_recurrence, 'Tumor Size No Recurrence')
], showfliers=False)
От трите boxplot графики, наредени една до друга се забелязва по-съсредоточено разпределение на данни около медианата във втората графика Tumor Size Recurrence, където са всички жени, при които се наблюдава болестта повторно.
Първата и третата графика имат сходна структура с по-разпръснати наблюдения, тоест по-голям интервал между Xmin и Xmax.
In [23]:
df['mean-age'] = df.age.apply(get_interval_mean)
df['mean-tumor'] = df['tumor-size'].apply(get_interval_mean)
def mean_tumor_by_age(df):
return df[['mean-age', 'mean-tumor']].groupby('mean-age').mean().reset_index()
In [24]:
# 1) За всички жени, заболели от рак
df_tumor_by_age = mean_tumor_by_age(df)
plot = df_tumor_by_age.plot.scatter(x='mean-age', y='mean-tumor')
In [25]:
# 2) Само за жени, за които е забелязано повтаряне на рака след проведената операция.
df_tumor_by_age_rec = mean_tumor_by_age(df[df['class'] == ' recurrence-events'])
plot = df_tumor_by_age_rec.plot.scatter(x='mean-age', y='mean-tumor')
In [26]:
# 3) Само за жени, за които не е забелязано повтаряне на рака след отстраняване на тумора.
df_tumor_by_age_no_rec = mean_tumor_by_age(df[df['class'] == ' no-recurrence-events'])
plot = df_tumor_by_age_no_rec.plot.scatter(x='mean-age', y='mean-tumor')
Първата и третата графика на разсейване не се отличават една от друга много. Това са цялата група от жени болни от рак и съответно групата от жени, при които не се е наблюдавал рак повторно.
Интересна е втората графика, при която има само жени с повторно наблюдаване на заболяването, където размерът на тумора и възрастта на жените имат лека корелация. Тоест при по-възрастни жени, се наблюдава по-голям размер на тумора.
In [27]:
# 1) За всички жени, заболели от рак
df_tumor_by_age.corr()
Out[27]:
In [28]:
# 2) Само за жени, за които е забелязано повтаряне на рака след проведената операция.
df_tumor_by_age_rec.corr()
Out[28]:
In [29]:
# 3) Само за жени, да които не е забелязано повтаряне на рака след отстраняване на тумора.
df_tumor_by_age_no_rec.corr()
Out[29]:
На първият scatter plot и на втория също се забелязва, че жени под 30 години имат много голям среден размер на тумора над 36, който се отличава значително повече от останалите стойности. Тази аномалия не се забелязва във втората група от жени, при които се наблюдава повтаряне на болестта.
При втората група пък жените в най-напреднала възраст над 70 години имат най-малък среден размер на тумора под 20, но преобладаващата средна стойност на размера на тумора е по-голяма с 2 до 4 единици повече от другите две групи от жени.
Тези аномалии влияят за отрицателната корелация разгледана по-долу.
При цялата група от жени с рак и при групата от жени без повторно наблюдаване на болестта се забелязва най-силна обратна корелация, тоест с нарастването на средната възраст намалява средния размер на тумора. Същата обратна корелация се наблюдава и във втората група, но в по-малка степен.