|
35 | 35 | from lifelines import KaplanMeierFitter |
36 | 36 | from lifelines.utils import concordance_index |
37 | 37 | from lifelines import CoxPHFitter |
| 38 | +from lifelines.statistics import logrank_test, multivariate_logrank_test |
| 39 | + |
| 40 | +from plotnine import ( |
| 41 | + ggplot, aes, geom_step, labs, theme_minimal, |
| 42 | + ggtitle, annotate, theme, element_text, |
| 43 | + scale_color_manual, scale_color_gradient, scale_color_brewer |
| 44 | +) |
| 45 | + |
38 | 46 |
|
39 | 47 | from sklearn.cluster import KMeans |
40 | 48 | from sklearn.metrics import silhouette_score |
|
45 | 53 | from sklearn.preprocessing import StandardScaler |
46 | 54 | import ot |
47 | 55 |
|
48 | | -from plotnine import ggplot, aes, geom_point, scale_color_manual, scale_color_gradient, labs, theme_minimal |
49 | 56 |
|
50 | 57 | def plot_dim_reduced(matrix, labels, method='pca', color_type='categorical'): |
51 | 58 | """ |
@@ -801,42 +808,59 @@ def print_summary_stats(dataset): |
801 | 808 |
|
802 | 809 | def plot_kaplan_meier_curves(durations, events, categorical_variable): |
803 | 810 | """ |
804 | | - Plots Kaplan-Meier survival curves for different groups defined by a categorical variable. |
805 | | -
|
806 | | - Parameters: |
807 | | - - durations: An array-like object of survival times or durations. |
808 | | - - events: An array-like object indicating whether an event (e.g., death) occurred (1) or was censored (0). |
809 | | - - categorical_variable: An array-like object defining groups for plotting different survival curves. |
| 811 | + Plots Kaplan-Meier survival curves using plotnine and annotates log-rank test p-values. |
810 | 812 | """ |
811 | | - # Initialize the Kaplan-Meier fitter |
812 | | - kmf = KaplanMeierFitter() |
813 | | - |
814 | | - # Ensure data is in a pandas DataFrame for easy handling |
| 813 | + # Prepare DataFrame |
815 | 814 | data = pd.DataFrame({ |
816 | 815 | 'Duration': durations, |
817 | 816 | 'Event': events, |
818 | 817 | 'Group': categorical_variable |
819 | 818 | }) |
820 | | - |
821 | | - # Plot survival curves for each category |
822 | | - plt.figure(figsize=(10, 6)) |
| 819 | + |
| 820 | + kmf = KaplanMeierFitter() |
| 821 | + survival_curves = [] |
| 822 | + |
| 823 | + # Fit Kaplan-Meier for each group and collect survival data |
| 824 | + for group in data['Group'].unique(): |
| 825 | + group_data = data[data['Group'] == group] |
| 826 | + kmf.fit(group_data['Duration'], group_data['Event'], label=str(group)) |
| 827 | + surv_df = kmf.survival_function_.reset_index() |
| 828 | + surv_df.columns = ['Time', 'Survival'] |
| 829 | + surv_df['Group'] = str(group) |
| 830 | + survival_curves.append(surv_df) |
| 831 | + |
| 832 | + # Combine all curves |
| 833 | + plot_data = pd.concat(survival_curves) |
| 834 | + |
| 835 | + # Compute log-rank p-value |
823 | 836 | categories = data['Group'].unique() |
824 | | - for category in categories: |
825 | | - # Select data for the group |
826 | | - group_data = data[data['Group'] == category] |
827 | | - |
828 | | - # Fit the model |
829 | | - kmf.fit(durations=group_data['Duration'], event_observed=group_data['Event'], label=str(category)) |
830 | | - |
831 | | - # Plot the survival curve for the group |
832 | | - kmf.plot_survival_function() |
833 | | - |
834 | | - plt.title('Kaplan-Meier Survival Curves by Group') |
835 | | - plt.xlabel('Time') |
836 | | - plt.ylabel('Survival Probability') |
837 | | - plt.legend(title='Group') |
838 | | - plt.grid(True) |
839 | | - plt.show() |
| 837 | + p_text = "" |
| 838 | + if len(categories) == 2: |
| 839 | + group1 = data[data['Group'] == categories[0]] |
| 840 | + group2 = data[data['Group'] == categories[1]] |
| 841 | + result = logrank_test(group1['Duration'], group2['Duration'], |
| 842 | + event_observed_A=group1['Event'], |
| 843 | + event_observed_B=group2['Event']) |
| 844 | + p_text = f"Log-rank p = {result.p_value:.4f}" |
| 845 | + elif len(categories) > 2: |
| 846 | + result = multivariate_logrank_test(data['Duration'], data['Group'], data['Event']) |
| 847 | + p_text = f"Multivariate log-rank p = {result.p_value:.4f}" |
| 848 | + else: |
| 849 | + p_text = "Only one group — log-rank test not applicable" |
| 850 | + |
| 851 | + # Create plot |
| 852 | + p = ( |
| 853 | + ggplot(plot_data, aes(x='Time', y='Survival', color='Group')) |
| 854 | + + geom_step() |
| 855 | + + labs(x='Time', y='Survival Probability', color='Group') |
| 856 | + + ggtitle('Kaplan-Meier Survival Curves by Group') |
| 857 | + + annotate("text", x=plot_data['Time'].max() * 0.6, y=0.1, label=p_text, size=10, ha='left') |
| 858 | + + theme_minimal() |
| 859 | + + theme(legend_title=element_text(size=10, weight='bold')) |
| 860 | + + scale_color_brewer(type='qual', palette='Set1') |
| 861 | + ) |
| 862 | + |
| 863 | + return p |
840 | 864 |
|
841 | 865 |
|
842 | 866 | def plot_hazard_ratios(cox_model): |
|
0 commit comments