<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts | Sebastian Pölsterl</title><link>https://k-d-w.org/post/</link><atom:link href="https://k-d-w.org/post/index.xml" rel="self" type="application/rss+xml"/><description>Posts</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><copyright>© Sebastian Pölsterl 2026</copyright><lastBuildDate>Sun, 05 Jul 2026 18:22:05 +0200</lastBuildDate><image><url>https://k-d-w.org/img/icon-192.png</url><title>Posts</title><link>https://k-d-w.org/post/</link></image><item><title>scikit-survival 0.28.0 released</title><link>https://k-d-w.org/blog/2026/07/scikit-survival-0.28.0-released/</link><pubDate>Sun, 05 Jul 2026 18:22:05 +0200</pubDate><guid>https://k-d-w.org/blog/2026/07/scikit-survival-0.28.0-released/</guid><description>&lt;p&gt;I am pleased to announce the release of &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/stable/release_notes/v0.28.html#scikit-survival-0-28-0-2026-07-05&#34; target=&#34;_blank&#34;&gt;scikit-survival 0.28.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A highlight of this release is the support for &lt;em&gt;Polars DataFrames&lt;/em&gt; alongside pandas DataFrames via
the &lt;a href=&#34;https://narwhals-dev.github.io/narwhals/&#34; target=&#34;_blank&#34;&gt;Narwhals&lt;/a&gt; dataframe abstraction layer.
In addition, this release adds support for &lt;em&gt;scikit-learn 1.9&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;support-for-polars&#34;&gt;Support for Polars&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.pola.rs/&#34; target=&#34;_blank&#34;&gt;Polars&lt;/a&gt; is a data frame library similar to &lt;a href=&#34;https://pandas.pydata.org/&#34; target=&#34;_blank&#34;&gt;pandas&lt;/a&gt;,
but with its core written in Rust instead of Python, which often gives Polars an advantage in terms of performance.&lt;/p&gt;
&lt;p&gt;All datasets shipped with scikit-survival can now be loaded as a Polars DataFrame by specifying the
&lt;code&gt;output_type&lt;/code&gt; argument.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.datasets import load_gbsg2
# return X as a polars DataFrame
X, y = load_gbsg2(output_type=&amp;quot;polars&amp;quot;)
X.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;shape: (5, 8)
┌──────┬────────┬───────┬──────────┬────────┬─────────┬────────┬───────┐
│ age ┆ estrec ┆ horTh ┆ menostat ┆ pnodes ┆ progrec ┆ tgrade ┆ tsize │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ enum ┆ enum ┆ f64 ┆ f64 ┆ enum ┆ f64 │
╞══════╪════════╪═══════╪══════════╪════════╪═════════╪════════╪═══════╡
│ 70.0 ┆ 66.0 ┆ no ┆ Post ┆ 3.0 ┆ 48.0 ┆ II ┆ 21.0 │
│ 56.0 ┆ 77.0 ┆ yes ┆ Post ┆ 7.0 ┆ 61.0 ┆ II ┆ 12.0 │
│ 58.0 ┆ 271.0 ┆ yes ┆ Post ┆ 9.0 ┆ 52.0 ┆ II ┆ 35.0 │
│ 59.0 ┆ 29.0 ┆ yes ┆ Post ┆ 4.0 ┆ 60.0 ┆ II ┆ 17.0 │
│ 73.0 ┆ 65.0 ┆ no ┆ Post ┆ 1.0 ┆ 26.0 ┆ II ┆ 35.0 │
└──────┴────────┴───────┴──────────┴────────┴─────────┴────────┴───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;transforming-dataframes&#34;&gt;Transforming DataFrames&lt;/h3&gt;
&lt;p&gt;scikit-learn enables transformers to return Polars data frames via the
&lt;a href=&#34;https://scikit-learn.org/stable/modules/df_output_transform.html#df-output-transform&#34; target=&#34;_blank&#34;&gt;set_output API&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sklearn import set_config
from sklearn.preprocessing import StandardScaler
set_config(transform_output=&amp;quot;polars&amp;quot;)
# standarize the columns of a polars DataFrame
X_standarized = StandardScaler().fit_transform(
X.select(&amp;quot;age&amp;quot;, &amp;quot;tsize&amp;quot;)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;scikit-surival has two transformers that now accept polars and pandas data frames as input:
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.28.0/api/generated/sksurv.kernels.ClinicalKernelTransform.html&#34; target=&#34;_blank&#34;&gt;ClinicalKernelTransform&lt;/a&gt;, and
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.28.0/api/generated/sksurv.preprocessing.OneHotEncoder.html&#34; target=&#34;_blank&#34;&gt;OneHotEncoder&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;clinicalkerneltransform&#34;&gt;ClinicalKernelTransform&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ClinicalKernelTransform&lt;/code&gt; computes a kernel matrix, so the output will be a numpy array, as before.
With scikit-survival 0.28.0, it is aware of the
&lt;a href=&#34;https://docs.pola.rs/user-guide/concepts/data-types-and-structures/#appendix-full-data-types-table&#34; target=&#34;_blank&#34;&gt;polars colum types&lt;/a&gt;
&lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Categorical&lt;/code&gt;, &lt;code&gt;Enum&lt;/code&gt; and &lt;code&gt;Object&lt;/code&gt;.
However, polars does not have a concept similar to &lt;a href=&#34;https://pandas.pydata.org/docs/user_guide/categorical.html#sorting-and-order&#34; target=&#34;_blank&#34;&gt;ordered categories&lt;/a&gt; in pandas: &lt;code&gt;pd.Categorical([…], ordered=True)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When computing the clinical kernel for a polars data frame, you can specify the order
of categories with the &lt;code&gt;ordinal_categories&lt;/code&gt; argument, otherwise all non-numeric columns
will be treated as &lt;em&gt;nominal&lt;/em&gt; columns, where values have no specific order
(e.g. the column &lt;code&gt;horTh&lt;/code&gt; with values &amp;ldquo;yes&amp;rdquo; and &amp;ldquo;no&amp;rdquo; in the example below).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.kernels import ClinicalKernelTransform
K = ClinicalKernelTransform(
ordinal_categories={&amp;quot;tgrade&amp;quot;: [&amp;quot;I&amp;quot;, &amp;quot;II&amp;quot;, &amp;quot;III&amp;quot;]},
).fit_transform(
X.select(&amp;quot;age&amp;quot;, &amp;quot;horTh&amp;quot;, &amp;quot;tgrade&amp;quot;)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;onehotencoder&#34;&gt;OneHotEncoder&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;OneHotEncoder&lt;/code&gt; encodes the string-type columns to numeric columns.
It automatically returns a data frame of the same type as the input:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.preprocessing import OneHotEncoder
X_onehot = OneHotEncoder().fit_transform(X.select(&amp;quot;horTh&amp;quot;, &amp;quot;tgrade&amp;quot;))
X_onehot.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-text&#34;&gt;shape: (5, 3)
┌───────────┬───────────┬────────────┐
│ horTh=yes ┆ tgrade=II ┆ tgrade=III │
│ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f64 │
╞═══════════╪═══════════╪════════════╡
│ 0.0 ┆ 1.0 ┆ 0.0 │
│ 1.0 ┆ 1.0 ┆ 0.0 │
│ 1.0 ┆ 1.0 ┆ 0.0 │
│ 1.0 ┆ 1.0 ┆ 0.0 │
│ 0.0 ┆ 1.0 ┆ 0.0 │
└───────────┴───────────┴────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;fitting-survival-models&#34;&gt;Fitting survival models&lt;/h3&gt;
&lt;p&gt;Finally, you can pass a polars data frame to the fit and predict functions of any estimator.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sklearn.pipeline import make_pipeline
from sksurv.linear_model import CoxPHSurvivalAnalysis
pipe = make_pipeline(
OneHotEncoder(), StandardScaler(), CoxPHSurvivalAnalysis()
)
pipe.fit(X[:500], y[:500])
risk_scores = pipe.predict(X[500:])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this only works for &lt;em&gt;eager&lt;/em&gt; data frames, &lt;em&gt;lazy&lt;/em&gt; data frames will give an error.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;X_lazy = X.lazy()
pipe.fit(X_lazy[:500], y[:500])
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python-traceback&#34;&gt;Traceback (most recent call last):
File &amp;quot;example.py&amp;quot;, line 21, in &amp;lt;module&amp;gt;
pipe.fit(X_lazy[:500], y[:500])
File &amp;quot;…/site-packages/sklearn/base.py&amp;quot;, line 1403, in wrapper
return fit_method(estimator, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
…
File &amp;quot;…/site-packages/sksurv/_dataframe/_input.py&amp;quot;, line 96, in ensure_eager_dataframe
_reject_polars_lazyframe(obj)
File &amp;quot;…/site-packages/sksurv/_dataframe/_input.py&amp;quot;, line 84, in _reject_polars_lazyframe
raise TypeError(_LAZYFRAME_NOT_SUPPORTED_MSG)
TypeError: polars.LazyFrame is not supported; call .collect() before passing to scikit-survival.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;new-contributors&#34;&gt;New Contributors&lt;/h2&gt;
&lt;p&gt;A big shoutout to our two new contributors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/cakedev0&#34; target=&#34;_blank&#34;&gt;cakedev0&lt;/a&gt; for adding support for scikit-learn 1.9&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/55Kamiryo&#34; target=&#34;_blank&#34;&gt;55Kamiryo&lt;/a&gt; for adding support for Polars data frames.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;updated-dependencies&#34;&gt;Updated Dependencies&lt;/h2&gt;
&lt;p&gt;With this release, the minimum supported version are:&lt;/p&gt;
&lt;table style=&#34;width: auto&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Package&lt;/th&gt;&lt;th&gt;Minimum Version&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;narwhals&lt;/td&gt;&lt;td style=&#34;text-align: right&#34;&gt;2.0.1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;scikit-learn&lt;/td&gt;&lt;td style=&#34;text-align: right&#34;&gt;1.9.0&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;install&#34;&gt;Install&lt;/h2&gt;
&lt;p&gt;scikit-survival is available for Linux, macOS, and Windows
and can be installed either&lt;/p&gt;
&lt;p&gt;via pip:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or via conda&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; conda install -c conda-forge scikit-survival
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>scikit-survival 0.26.0 released</title><link>https://k-d-w.org/blog/2025/12/scikit-survival-0.26.0-released/</link><pubDate>Wed, 17 Dec 2025 21:26:48 +0100</pubDate><guid>https://k-d-w.org/blog/2025/12/scikit-survival-0.26.0-released/</guid><description>&lt;p&gt;I am pleased to announce that &lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.26.0 has been released.&lt;/p&gt;
&lt;p&gt;This is a maintainance release that adds support for Python 3.14 and
includes updates to make scikit-survival compatible with new versions
of pandas and osqp.
It adds support for the &lt;a href=&#34;https://pandas.pydata.org/docs/user_guide/migration-3-strings.html#string-migration-guide&#34; target=&#34;_blank&#34;&gt;pandas string dtype&lt;/a&gt;,
and &lt;a href=&#34;https://pandas.pydata.org/docs/user_guide/copy_on_write.html#copy-on-write&#34; target=&#34;_blank&#34;&gt;copy-on-write&lt;/a&gt;, which is going to become the default with pandas 3.
In addition, &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.26.0/api/generated/sksurv.preprocessing.OneHotEncoder.html#sksurv.preprocessing.OneHotEncoder&#34; title=&#34;sksurv.preprocessing.OneHotEncoder&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;sksurv.preprocessing.OneHotEncoder&lt;/code&gt;&lt;/a&gt;
now supports converting columns with the object dtype.&lt;/p&gt;
&lt;p&gt;With this release, the minimum supported version are:&lt;/p&gt;
&lt;table style=&#34;width: auto&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Package&lt;/th&gt;&lt;th&gt;Minimum Version&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Python&lt;/td&gt;&lt;td style=&#34;text-align: right&#34;&gt;3.11&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pandas&lt;/td&gt;&lt;td style=&#34;text-align: right&#34;&gt;2.0.0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;osqp&lt;/td&gt;&lt;td style=&#34;text-align: right&#34;&gt;1.0.2&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;install&#34;&gt;Install&lt;/h2&gt;
&lt;p&gt;scikit-survival is available for Linux, macOS, and Windows
and can be installed either&lt;/p&gt;
&lt;p&gt;via pip:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or via conda&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; conda install -c conda-forge scikit-survival
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>scikit-survival 0.25.0 with improved documentation released</title><link>https://k-d-w.org/blog/2025/08/scikit-survival-0.25.0-with-improved-documentation-released/</link><pubDate>Fri, 22 Aug 2025 23:55:06 +0200</pubDate><guid>https://k-d-w.org/blog/2025/08/scikit-survival-0.25.0-with-improved-documentation-released/</guid><description>&lt;p&gt;I am pleased to announce that &lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.25.0 has been released.&lt;/p&gt;
&lt;p&gt;This release adds support for scikit-learn 1.7, in addition to version 1.6.
However, the most significant changes in this release affect the documentation.
The &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/index.html&#34; target=&#34;_blank&#34;&gt;API documentation&lt;/a&gt; has been completely overhauled to improve clarity and consistency.
I hope this marks a significant improvement for users new to scikit-survival.&lt;/p&gt;
&lt;p&gt;One of the biggest pain points for users seems to be understanding which metric can be used to evaluate the performance of a given estimator.
The &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/user_guide/evaluating-survival-models.html&#34; target=&#34;_blank&#34;&gt;user guide&lt;/a&gt;
now summarizes the different options.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2025/08/scikit-survival-0.25.0-with-improved-documentation-released/img/metrics-diagram.svg&#34;/&gt;
&lt;/figure&gt;
&lt;h2 id=&#34;which-performance-metrics-exist&#34;&gt;Which Performance Metrics Exist?&lt;/h2&gt;
&lt;p&gt;The performance metrics for evaluating survival models can be broadly divided into three groups:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concordance Index (C-index)&lt;/strong&gt;: Measures the rank correlation between predicted risk scores and observed event times.
Two implementations are available in scikit-survival:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.metrics.concordance_index_censored.html&#34; target=&#34;_blank&#34;&gt;concordance_index_censored()&lt;/a&gt;:
This implements Harrell&amp;rsquo;s estimator, which can be
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/user_guide/evaluating-survival-models.html#Bias-of-Harrell&#39;s-Concordance-Index&#34; target=&#34;_blank&#34;&gt;optimistic with high censoring&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.metrics.concordance_index_ipcw.html&#34; target=&#34;_blank&#34;&gt;concordance_index_ipcw()&lt;/a&gt;:
An inverse probability of censoring weighted (IPCW) alternative that provides a less biased estimate, especially with high censoring.
It is the preferred estimator of the C-Index.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cumulative/Dynamic Area Under the ROC Curve (AUC)&lt;/strong&gt;:
Extends the AUC to survival data, quantifying how well a model distinguishes subjects who experience an event by a given time from those who do not. It can handle &lt;em&gt;time-dependent risk scores&lt;/em&gt;
and is implemented in &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.metrics.cumulative_dynamic_auc.html&#34; target=&#34;_blank&#34;&gt;cumulative_dynamic_auc()&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Brier Score&lt;/strong&gt;:
An extension of the mean squared error to right-censored data.
The Brier score assesses both discrimination and calibration based on a model&amp;rsquo;s estimated survival functions.
You can either compute the Brier score at specific time point(s) using
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.metrics.brier_score.html&#34; target=&#34;_blank&#34;&gt;brier_score()&lt;/a&gt;
or compute an overall measure by integrating the Brier score over a range of time points via
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.metrics.integrated_brier_score.html&#34; target=&#34;_blank&#34;&gt;integrated_brier_score()&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;what-do-survival-models-predict&#34;&gt;What Do Survival Models Predict?&lt;/h2&gt;
&lt;p&gt;Survival models can predict several quantities, depending on the model being used.
First of all, every estimator has a &lt;code&gt;predict()&lt;/code&gt; method,
which either returns a unit-less risk score
or the predicted time of an event.&lt;/p&gt;
&lt;p&gt;If predictions are &lt;em&gt;risk scores&lt;/em&gt;, higher values indicate an
increased risk of experiencing an event. The scores have no unit
and are only meaningful for ranking samples by their risk of experiencing an event.
This is for example the case for
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.linear_model.CoxPHSurvivalAnalysis.html#sksurv.linear_model.CoxPHSurvivalAnalysis.predict&#34; target=&#34;_blank&#34;&gt;CoxPHSurvivalAnalysis&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.datasets import load_veterans_lung_cancer
from sksurv.linear_model import CoxPHSurvivalAnalysis
from sksurv.metrics import concordance_index_censored
from sksurv.preprocessing import OneHotEncoder
# Load data
X, y = load_veterans_lung_cancer()
Xt = OneHotEncoder().fit_transform(X)
# Fit model
estimator = CoxPHSurvivalAnalysis().fit(Xt, y)
# Predict risk score
predicted_risk = estimator.predict(Xt)
# Evaluate risk scores
cindex = concordance_index_censored(
y[&amp;quot;Status&amp;quot;], y[&amp;quot;Survival_in_days&amp;quot;], predicted_risk
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If predictions directly relate to the time point of an event,
lower scores indicate shorter survival, while higher scores indicate longer survival.
See for example &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.linear_model.IPCRidge.html#sksurv.linear_model.IPCRidge.predict&#34; target=&#34;_blank&#34;&gt;IPCRidge&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.datasets import load_veterans_lung_cancer
from sksurv.linear_model import IPCRidge
from sksurv.metrics import concordance_index_censored
from sksurv.preprocessing import OneHotEncoder
# Load the data
X, y = load_veterans_lung_cancer()
Xt = OneHotEncoder().fit_transform(X)
# Fit the model
estimator = IPCRidge().fit(Xt, y)
# Predict time of an event
predicted_time = estimator.predict(Xt)
# Flip sign of predictions to obtain a risk score
cindex = concordance_index_censored(
y[&amp;quot;Status&amp;quot;], y[&amp;quot;Survival_in_days&amp;quot;], -1 * predicted_time
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both types of predictions can be evaluated by
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.metrics.cumulative_dynamic_auc.html&#34; target=&#34;_blank&#34;&gt;cumulative_dynamic_auc()&lt;/a&gt; too
but &lt;em&gt;not the Brier score&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;While the concordance index is easy to interpret,
it is not a useful measure of performance if a specific time range
is of primary interest (e.g. predicting death within 2 years).
This is particularly relevant for survival models that can
make &lt;em&gt;time-dependent predictions&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For instance,
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.ensemble.RandomSurvivalForest.html&#34; target=&#34;_blank&#34;&gt;RandomSurvivalForest&lt;/a&gt;,
can also predict survival functions (via &lt;code&gt;predict_survival_function()&lt;/code&gt;)
or cumulative hazard functions (via &lt;code&gt;predict_cumulative_hazard_function()&lt;/code&gt;).
These functions return lists of
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.functions.StepFunction.html&#34; target=&#34;_blank&#34;&gt;StepFunction&lt;/a&gt; instances.
Each instance can be evaluated at a set of time points to obtain predicted
survival probabilities (or cumulative hazards).
The Brier score and
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/generated/sksurv.metrics.cumulative_dynamic_auc.html&#34; target=&#34;_blank&#34;&gt;cumulative_dynamic_auc()&lt;/a&gt;
are capable of evaluating time-dependent predictions, but &lt;em&gt;not the C-Index&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import numpy as np
from sksurv.datasets import load_veterans_lung_cancer
from sksurv.ensemble import RandomSurvivalForest
from sksurv.metrics import integrated_brier_score
from sksurv.preprocessing import OneHotEncoder
# Load the data
X, y = load_veterans_lung_cancer()
Xt = OneHotEncoder().fit_transform(X)
# Fit the model
estimator = RandomSurvivalForest().fit(Xt, y)
# predict survival functions
surv_funcs = estimator.predict_survival_function(Xt)
# select time points to evaluate performance at
times = np.arange(7, 365)
# create predictions at selected time points
preds = np.asarray(
[[sfn(t) for t in times] for sfn in surv_funcs]
)
# compute integral
score = integrated_brier_score(y, y, preds, times)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more details on evaluating survival models, please have a look at the
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/user_guide/evaluating-survival-models.html&#34; target=&#34;_blank&#34;&gt;user guide&lt;/a&gt;
and the &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.25.0/api/index.html&#34; target=&#34;_blank&#34;&gt;API documentation&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>scikit-survival 0.24.0 released</title><link>https://k-d-w.org/blog/2025/02/scikit-survival-0.24.0-released/</link><pubDate>Wed, 26 Feb 2025 22:26:45 +0100</pubDate><guid>https://k-d-w.org/blog/2025/02/scikit-survival-0.24.0-released/</guid><description>&lt;p&gt;It&amp;rsquo;s my pleasure to announce the release of &lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.24.0.&lt;/p&gt;
&lt;p&gt;A highlight of this release the addition of
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.24.0/api/generated/sksurv.nonparametric.cumulative_incidence_competing_risks.html#sksurv.nonparametric.cumulative_incidence_competing_risks&#34; target=&#34;_blank&#34;&gt;cumulative_incidence_competing_risks()&lt;/a&gt;
which implements a non-parameteric estimator of the cumulative incidence function in the presence of competing risks.
In addition, the release adds support for scikit-learn 1.6, including the support for missing values for
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.24.0/api/generated/sksurv.ensemble.ExtraSurvivalTrees.html#sksurv.ensemble.ExtraSurvivalTrees&#34; target=&#34;_blank&#34;&gt;ExtraSurvivalTrees&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;analysis-of-competing-risks&#34;&gt;Analysis of Competing Risks&lt;/h2&gt;
&lt;p&gt;In classical survival analysis, the focus is on the time until a specific event occurs. If no event is observed during the study period, the time of the event is considered censored. A common assumption is that censoring is non-informative, meaning that censored subjects have a similar prognosis to those who were not censored.&lt;/p&gt;
&lt;p&gt;Competing risks arise when each subject can experience an event due to one of $K$ ($K \geq 2$) mutually exclusive causes, termed competing risks. Thus, the occurrence of one event prevents the occurrence of other events. For example, after a bone marrow transplant, a patient might relapse or die from transplant-related causes (transplant-related mortality). In this case, death from transplant-related mortality precludes relapse.&lt;/p&gt;
&lt;p&gt;The bone marrow transplant data from &lt;a href=&#34;https://doi.org/10.1038/sj.bmt.1705727&#34; target=&#34;_blank&#34;&gt;Scrucca et al., Bone Marrow Transplantation (2007)&lt;/a&gt; includes data
from 35 patients grouped into two cancer types: Acute Lymphoblastic Leukemia (ALL; coded as 0), and Acute Myeloid Leukemia (AML; coded as 1).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.datasets import load_bmt
bmt_features, bmt_outcome = load_bmt()
diseases = bmt_features[&amp;quot;dis&amp;quot;].cat.rename_categories(
{&amp;quot;0&amp;quot;: &amp;quot;ALL&amp;quot;, &amp;quot;1&amp;quot;: &amp;quot;AML&amp;quot;}
)
diseases.value_counts().to_frame()
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&#34;table-responsive&#34;&gt;
&lt;table class=&#34;dataframe&#34; style=&#34;width: 20%;&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;dis&lt;/th&gt;
&lt;th&gt;count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&#34;text-align: right;&#34;&gt;
&lt;th&gt;AML&lt;/th&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&#34;text-align: right;&#34;&gt;
&lt;th&gt;ALL&lt;/th&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;During the follow-up period, some patients might experience a relapse of the original leukemia or die
while in remission (transplant related death).
The outcome is defined similarly to standard time-to-event data, except that the event indicator specifies the type of event, where 0 always indicates censoring.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import pandas as pd
status_labels = {
0: &amp;quot;Censored&amp;quot;,
1: &amp;quot;Transplant related mortality&amp;quot;,
2: &amp;quot;Relapse&amp;quot;,
}
risks = pd.DataFrame.from_records(bmt_outcome).assign(
label=lambda x: x[&amp;quot;status&amp;quot;].replace(status_labels)
)
risks[&amp;quot;label&amp;quot;].value_counts().to_frame()
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&#34;table-responsive&#34;&gt;
&lt;table class=&#34;dataframe&#34; style=&#34;width: 40%;&#34;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;label&lt;/th&gt;
&lt;th&gt;count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&#34;text-align: right;&#34;&gt;
&lt;th&gt;Relapse&lt;/th&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&#34;text-align: right;&#34;&gt;
&lt;th&gt;Censored&lt;/th&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&#34;text-align: right;&#34;&gt;
&lt;th&gt;Transplant related mortality&lt;/th&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The table above shows the number of observations for each status.&lt;/p&gt;
&lt;h3 id=&#34;non-parametric-estimator-of-the-cumulative-incidence-function&#34;&gt;Non-parametric Estimator of the Cumulative Incidence Function&lt;/h3&gt;
&lt;p&gt;If the goal is to estimate the probability of relapse, transplant-related death is a competing risk event. This means that the occurrence of relapse prevents the occurrence of transplant-related death, and vice versa. We aim to estimate curves that illustrate how the likelihood of these events changes over time.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s begin by estimating the probability of relapse using the complement of the Kaplan-Meier estimator. With this approach, we treat deaths as censored observations. One minus the Kaplan-Meier estimator provides an estimate of the probability of relapse before time $t$.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import matplotlib.pyplot as plt
from sksurv.nonparametric import kaplan_meier_estimator
times, km_estimate = kaplan_meier_estimator(
bmt_outcome[&amp;quot;status&amp;quot;] == 1, bmt_outcome[&amp;quot;ftime&amp;quot;]
)
plt.step(times, 1 - km_estimate, where=&amp;quot;post&amp;quot;)
plt.xlabel(&amp;quot;time $t$&amp;quot;)
plt.ylabel(&amp;quot;Probability of relapsing before time $t$&amp;quot;)
plt.ylim(0, 1)
plt.grid()
&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2025/02/scikit-survival-0.24.0-released/img/bmt-kaplan-meier.svg&#34; width=&#34;400&#34;/&gt;
&lt;/figure&gt;
&lt;p&gt;However, this approach has a significant drawback: considering death as a censoring event violates the assumption that censoring is non-informative. This is because patients who died from transplant-related mortality have a different prognosis than patients who did not experience any event. Therefore, the estimated probability of relapse is often biased.&lt;/p&gt;
&lt;p&gt;The cause-specific &lt;strong&gt;cumulative incidence function (CIF)&lt;/strong&gt; addresses this problem by estimating the cause-specific hazard of each event separately. The cumulative incidence function estimates the probability that the event of interest occurs before time $t$, and that it occurs before any of the competing causes of an event. In the bone marrow transplant dataset, the cumulative incidence function of relapse indicates the probability of relapse before time $t$, given that the patient has not died from other causes before time $t$.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.nonparametric import cumulative_incidence_competing_risks
times, cif_estimates = cumulative_incidence_competing_risks(
bmt_outcome[&amp;quot;status&amp;quot;], bmt_outcome[&amp;quot;ftime&amp;quot;]
)
plt.step(times, cif_estimates[0], where=&amp;quot;post&amp;quot;, label=&amp;quot;Total risk&amp;quot;)
for i, cif in enumerate(cif_estimates[1:], start=1):
plt.step(times, cif, where=&amp;quot;post&amp;quot;, label=status_labels[i])
plt.legend()
plt.xlabel(&amp;quot;time $t$&amp;quot;)
plt.ylabel(&amp;quot;Probability of event before time $t$&amp;quot;)
plt.ylim(0, 1)
plt.grid()
&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2025/02/scikit-survival-0.24.0-released/img/bmt-cumulative-incidence.svg&#34; width=&#34;400&#34;/&gt;
&lt;/figure&gt;
&lt;p&gt;The plot shows the estimated probability of experiencing an event at time $t$ for both the individual risks and for the total risk.&lt;/p&gt;
&lt;p&gt;Next, we want to to estimate the cumulative incidence curves for the two cancer types — acute lymphoblastic leukemia (ALL) and acute myeloid leukemia (AML) — to examine how the probability of relapse depends on the original disease diagnosis.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;_, axs = plt.subplots(2, 2, figsize=(7, 6), sharex=True, sharey=True)
for j, disease in enumerate(diseases.unique()):
mask = diseases == disease
event = bmt_outcome[&amp;quot;status&amp;quot;][mask]
time = bmt_outcome[&amp;quot;ftime&amp;quot;][mask]
times, cif_estimates, conf_int = cumulative_incidence_competing_risks(
event,
time,
conf_type=&amp;quot;log-log&amp;quot;,
)
for i, (cif, ci, ax) in enumerate(
zip(cif_estimates[1:], conf_int[1:], axs[:, j]), start=1
):
ax.step(times, cif, where=&amp;quot;post&amp;quot;)
ax.fill_between(times, ci[0], ci[1], alpha=0.25, step=&amp;quot;post&amp;quot;)
ax.set_title(f&amp;quot;{disease}: {status_labels[i]}&amp;quot;, size=&amp;quot;small&amp;quot;)
ax.grid()
for ax in axs[-1, :]:
ax.set_xlabel(&amp;quot;time $t$&amp;quot;)
for ax in axs[:, 0]:
ax.set_ylim(0, 1)
ax.set_ylabel(&amp;quot;Probability of event before time $t$&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2025/02/scikit-survival-0.24.0-released/img/bmt-cumulative-incidence-by-diagnosis.svg&#34; width=&#34;450&#34;/&gt;
&lt;/figure&gt;
&lt;p&gt;The left column shows the estimated cumulative incidence curves (solid lines) for patients diagnosed with ALL, while the right column shows the curves for patients diagnosed with AML, along with their 95% pointwise confidence intervals. The plot indicates that the estimated probability of relapse at $t=40$ days is more than three times higher for patients diagnosed with ALL compared to AML.&lt;/p&gt;
&lt;p&gt;If you want to run the examples above yourself, you can
&lt;a href=&#34;https://mybinder.org/v2/gh/sebp/scikit-survival/master?urlpath=lab/tree/notebooks/competing-risks.ipynb&#34; target=&#34;_blank&#34;&gt;execute them interactively in your browser using binder&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>scikit-survival 0.23.0 released</title><link>https://k-d-w.org/blog/2024/06/scikit-survival-0.23.0-released/</link><pubDate>Sun, 30 Jun 2024 13:36:08 +0200</pubDate><guid>https://k-d-w.org/blog/2024/06/scikit-survival-0.23.0-released/</guid><description>&lt;p&gt;I am pleased to announce the release of &lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.23.0.&lt;/p&gt;
&lt;p&gt;This release adds support for scikit-learn 1.4 and 1.5, which includes missing value support for &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.23.0/api/generated/sksurv.ensemble.RandomSurvivalForest.html#sksurv.ensemble.RandomSurvivalForest&#34; target=&#34;_blank&#34;&gt;RandomSurvivalForest&lt;/a&gt;.
For more details on missing values support, see the section
in the &lt;a href=&#34;https://k-d-w.org/blog/2023/10/scikit-survival-0.22.0-released/#missing-values-support-in-survivaltree&#34; target=&#34;_blank&#34;&gt;release announcement for 0.23.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Moreover, this release fixes critical bugs. When fitting &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.23.0/api/generated/sksurv.tree.SurvivalTree.html#sksurv.tree.SurvivalTree&#34; target=&#34;_blank&#34;&gt;SurvivalTree&lt;/a&gt;, the &lt;code&gt;sample_weight&lt;/code&gt; is now correctly considered when computing the log-rank statistic for each split. This change also affects &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.23.0/api/generated/sksurv.ensemble.RandomSurvivalForest.html#sksurv.ensemble.RandomSurvivalForest&#34; target=&#34;_blank&#34;&gt;RandomSurvivalForest&lt;/a&gt; and &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.23.0/api/generated/sksurv.ensemble.ExtraSurvivalTrees.html#sksurv.ensemble.ExtraSurvivalTrees&#34; target=&#34;_blank&#34;&gt;ExtraSurvivalTrees&lt;/a&gt; which pass &lt;code&gt;sample_weight&lt;/code&gt; to the individual trees in the ensemble. Therefore, the outputs produced by &lt;em&gt;SurvivalTree&lt;/em&gt;,
&lt;em&gt;RandomSurvivalForest&lt;/em&gt;, and &lt;em&gt;ExtraSurvivalTrees&lt;/em&gt; will differ from previous releases.&lt;/p&gt;
&lt;p&gt;This release fixes a bug in &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.23.0/api/generated/sksurv.ensemble.ComponentwiseGradientBoostingSurvivalAnalysis.html#sksurv.ensemble.ComponentwiseGradientBoostingSurvivalAnalysis&#34; target=&#34;_blank&#34;&gt;ComponentwiseGradientBoostingSurvivalAnalysis&lt;/a&gt; and &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.23.0/api/generated/sksurv.ensemble.GradientBoostingSurvivalAnalysis.html#sksurv.ensemble.GradientBoostingSurvivalAnalysis&#34; target=&#34;_blank&#34;&gt;GradientBoostingSurvivalAnalysis&lt;/a&gt; when dropout is used. Previously, dropout was only applied starting with the third iteration, now dropout is applied in the second iteration too.&lt;/p&gt;
&lt;p&gt;Finally, this release adds compatibility with numpy 2.0 and drops support for Python 3.8.&lt;/p&gt;
&lt;h2 id=&#34;install&#34;&gt;Install&lt;/h2&gt;
&lt;p&gt;scikit-survival is available for Linux, macOS, and Windows
and can be installed either&lt;/p&gt;
&lt;p&gt;via pip:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or via conda&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; conda install -c conda-forge scikit-survival
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>scikit-survival 0.22.0 released</title><link>https://k-d-w.org/blog/2023/10/scikit-survival-0.22.0-released/</link><pubDate>Mon, 02 Oct 2023 22:39:15 +0200</pubDate><guid>https://k-d-w.org/blog/2023/10/scikit-survival-0.22.0-released/</guid><description>&lt;p&gt;I am pleased to announce the release of &lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.22.0.
The highlights for this release include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compatibility with scikit-learn 1.3.&lt;/li&gt;
&lt;li&gt;Missing value support for &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.tree.SurvivalTree.html#sksurv.tree.SurvivalTree&#34; target=&#34;_blank&#34;&gt;SurvivalTree&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A reduced memory mode for &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.ensemble.RandomSurvivalForest.html#sksurv.ensemble.RandomSurvivalForest&#34; target=&#34;_blank&#34;&gt;RandomSurvivalForest&lt;/a&gt;, &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.ensemble.ExtraSurvivalTrees.html#sksurv.ensemble.ExtraSurvivalTrees&#34; target=&#34;_blank&#34;&gt;ExtraSurvivalTrees&lt;/a&gt;, and &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.tree.SurvivalTree.html#sksurv.tree.SurvivalTree&#34; target=&#34;_blank&#34;&gt;SurvivalTree&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Support for &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.meta.Stacking.html#sksurv.meta.Stacking.predict_cumulative_hazard_function&#34; target=&#34;_blank&#34;&gt;predict_cumulative_hazard_function()&lt;/a&gt; and &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.meta.Stacking.html#sksurv.meta.Stacking.predict_survival_function&#34; target=&#34;_blank&#34;&gt;predict_survival_function()&lt;/a&gt; in &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.meta.Stacking.html&#34; target=&#34;_blank&#34;&gt;Stacking&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;missing-values-support-in-survivaltree&#34;&gt;Missing Values Support in SurvivalTree&lt;/h2&gt;
&lt;p&gt;Based on the &lt;a href=&#34;https://scikit-learn.org/1.3/modules/tree.html#tree-missing-value-support&#34; target=&#34;_blank&#34;&gt;missing value support&lt;/a&gt;
in scikit-learn 1.3, a &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.tree.SurvivalTree.html#sksurv.tree.SurvivalTree&#34; target=&#34;_blank&#34;&gt;SurvivalTree&lt;/a&gt;
can now deal with missing values if it is fit with &lt;code&gt;splitter=&#39;best&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the training data contained no missing values, then during prediction missing values are mapped
to the child node with the most samples:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;X, y = load_veterans_lung_cancer()
X_train = np.asarray(X.loc[:, [&amp;quot;Karnofsky_score&amp;quot;]], dtype=np.float32)
est = SurvivalTree(max_depth=1)
est.fit(X_train, y)
X_test = np.array([[np.nan]])
surv_fn = est.predict_survival_function(X_test, return_array=True)
mask = X_train[:, 0] &amp;gt; est.tree_.threshold[0]
km_x, km_y = kaplan_meier_estimator(
y[mask][&amp;quot;Status&amp;quot;], y[mask][&amp;quot;Survival_in_days&amp;quot;]
)
plt.step(km_x, km_y, where=&amp;quot;post&amp;quot;, linewidth=5)
plt.step(
est.unique_times_, surv_fn[0], where=&amp;quot;post&amp;quot;, linewidth=3, linestyle=&amp;quot;dotted&amp;quot;
)
plt.ylim(0, 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2023/10/scikit-survival-0.22.0-released/img/survival-tree-missing-values-1.svg&#34; width=&#34;400&#34;/&gt;
&lt;/figure&gt;
&lt;p&gt;If a tree is fit to training data with missing values, the splitter will evaluate
each split with all samples with missing values going to the left or the right child node.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;X, y = load_veterans_lung_cancer()
X_train = np.asarray(X.loc[:, [&amp;quot;Age_in_years&amp;quot;]], dtype=np.float32)
X_train[-50:, :] = np.nan
est = SurvivalTree(max_depth=1)
est.fit(X_train, y)
X_test = np.array([[np.nan]])
surv_fn = est.predict_survival_function(X_test, return_array=True)
mask = X_train[:, 0] &amp;gt; est.tree_.threshold[0]
mask |= np.isnan(X_train[:, 0])
km_x, km_y = kaplan_meier_estimator(
y[mask][&amp;quot;Status&amp;quot;], y[mask][&amp;quot;Survival_in_days&amp;quot;]
)
plt.step(km_x, km_y, where=&amp;quot;post&amp;quot;, linewidth=5)
plt.step(
est.unique_times_, surv_fn[0], where=&amp;quot;post&amp;quot;, linewidth=3, linestyle=&amp;quot;dotted&amp;quot;
)
plt.ylim(0, 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2023/10/scikit-survival-0.22.0-released/img/survival-tree-missing-values-2.svg&#34; width=&#34;400&#34;/&gt;
&lt;/figure&gt;
&lt;p&gt;These rules are identical to those of &lt;a href=&#34;https://scikit-learn.org/1.3/modules/tree.html#tree-missing-value-support&#34; target=&#34;_blank&#34;&gt;scikit-learn&amp;rsquo;s missing value support&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;low-memory-mode-for-survivaltree-and-randomsurvivalforest&#34;&gt;Low-memory Mode for SurvivalTree and RandomSurvivalForest&lt;/h2&gt;
&lt;p&gt;The last release already saw
&lt;a href=&#34;https://k-d-w.org/blog/2023/06/scikit-survival-0.21.0-released/&#34; target=&#34;_blank&#34;&gt;performance improvments to SurvivalTree and RandomSurvivalForest&lt;/a&gt;.
This release adds the &lt;code&gt;low_memory&lt;/code&gt; option to
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.ensemble.RandomSurvivalForest.html#sksurv.ensemble.RandomSurvivalForest&#34; target=&#34;_blank&#34;&gt;RandomSurvivalForest&lt;/a&gt;, &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.ensemble.ExtraSurvivalTrees.html#sksurv.ensemble.ExtraSurvivalTrees&#34; target=&#34;_blank&#34;&gt;ExtraSurvivalTrees&lt;/a&gt;, and &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.22.0/api/generated/sksurv.tree.SurvivalTree.html#sksurv.tree.SurvivalTree&#34; target=&#34;_blank&#34;&gt;SurvivalTree&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If low-memory mode is &lt;em&gt;disabled&lt;/em&gt;, which is the default, calling &lt;code&gt;predict&lt;/code&gt; on a sample will require memory
in the order of unique event times in the training data, because the cumulative hazard function
is computed as an intermediate value.
If low-memory mode is &lt;em&gt;enabled&lt;/em&gt;, then the risk score is computed directly, without computing
the cumulative hazard function. However, low-memory mode disables using
&lt;code&gt;predict_cumulative_hazard_function&lt;/code&gt; and &lt;code&gt;predict_survival_function&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;install&#34;&gt;Install&lt;/h2&gt;
&lt;p&gt;Pre-built conda packages are available for Linux, macOS, and Windows, either&lt;/p&gt;
&lt;p&gt;via pip:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or via conda&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; conda install -c sebp scikit-survival
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>scikit-survival 0.21.0 released</title><link>https://k-d-w.org/blog/2023/06/scikit-survival-0.21.0-released/</link><pubDate>Sun, 11 Jun 2023 18:43:30 +0200</pubDate><guid>https://k-d-w.org/blog/2023/06/scikit-survival-0.21.0-released/</guid><description>&lt;p&gt;Today marks the release of &lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.21.0.
This release features some exciting new features and significant performance improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pointwise confidence intervals for the Kaplan-Meier estimator.&lt;/li&gt;
&lt;li&gt;Early stopping in GradientBoostingSurvivalAnalysis.&lt;/li&gt;
&lt;li&gt;Improved performance of fitting SurvivalTree and RandomSurvivalForest.&lt;/li&gt;
&lt;li&gt;Reduced memory footprint of concordance_index_censored.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;pointwise-confidence-intervals-for-the-kaplan-meier-estimator&#34;&gt;Pointwise Confidence Intervals for the Kaplan-Meier Estimator&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/api/generated/sksurv.nonparametric.kaplan_meier_estimator.html#sksurv.nonparametric.kaplan_meier_estimator&#34; target=&#34;_blank&#34;&gt;kaplan_meier_estimator()&lt;/a&gt;
can now estimate pointwise confidence intervals by specifying the &lt;code&gt;conf_type&lt;/code&gt; parameter.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import matplotlib.pyplot as plt
from sksurv.datasets import load_veterans_lung_cancer
from sksurv.nonparametric import kaplan_meier_estimator
_, y = load_veterans_lung_cancer()
time, survival_prob, conf_int = kaplan_meier_estimator(
y[&amp;quot;Status&amp;quot;], y[&amp;quot;Survival_in_days&amp;quot;], conf_type=&amp;quot;log-log&amp;quot;
)
plt.step(time, survival_prob, where=&amp;quot;post&amp;quot;)
plt.fill_between(time, conf_int[0], conf_int[1], alpha=0.25, step=&amp;quot;post&amp;quot;)
plt.ylim(0, 1)
plt.ylabel(&amp;quot;est. probability of survival $\hat{S}(t)$&amp;quot;)
plt.xlabel(&amp;quot;time $t$&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2023/06/scikit-survival-0.21.0-released/img/km-with-ci.svg&#34;
alt=&#34;Kaplan-Meier curve with pointwise confidence intervals.&#34;/&gt; &lt;figcaption&gt;
&lt;p&gt;Kaplan-Meier curve with pointwise confidence intervals.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&#34;early-stopping-in-gradientboostingsurvivalanalysis&#34;&gt;Early Stopping in GradientBoostingSurvivalAnalysis&lt;/h2&gt;
&lt;p&gt;Early stopping enables us to determine when the model is sufficiently complex.
This is usually done by continuously evaluating the model on held-out data.
For &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/api/generated/sksurv.ensemble.GradientBoostingSurvivalAnalysis.html#sksurv.ensemble.GradientBoostingSurvivalAnalysis&#34; target=&#34;_blank&#34;&gt;GradientBoostingSurvivalAnalysis&lt;/a&gt;,
the easiest way to achieve this is by setting &lt;code&gt;n_iter_no_change&lt;/code&gt; and
optionally &lt;code&gt;validation_fraction&lt;/code&gt; (defaults to 0.1).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from sksurv.datasets import load_whas500
from sksurv.ensemble import GradientBoostingSurvivalAnalysis
X, y = load_whas500()
model = GradientBoostingSurvivalAnalysis(
n_estimators=1000, max_depth=2, subsample=0.8, n_iter_no_change=10, random_state=0,
)
model.fit(X, y)
print(model.n_estimators_)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;model.n_estimators_&lt;/code&gt; indicates that fitting stopped after 73 iterations,
instead of the maximum 1000 iterations.&lt;/p&gt;
&lt;p&gt;Alternatively, one can provide a custom callback function to the
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/api/generated/sksurv.ensemble.GradientBoostingSurvivalAnalysis.html#sksurv.ensemble.GradientBoostingSurvivalAnalysis.fit&#34; target=&#34;_blank&#34;&gt;fit&lt;/a&gt;
method. If the callback returns &lt;code&gt;True&lt;/code&gt;, training is stopped.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;model = GradientBoostingSurvivalAnalysis(
n_estimators=1000, max_depth=2, subsample=0.8, random_state=0,
)
def early_stopping_monitor(iteration, model, args):
&amp;quot;&amp;quot;&amp;quot;Stop training if there was no improvement in the last 10 iterations&amp;quot;&amp;quot;&amp;quot;
start = max(0, iteration - 10)
end = iteration + 1
oob_improvement = model.oob_improvement_[start:end]
return all(oob_improvement &amp;lt; 0)
model.fit(X, y, monitor=early_stopping_monitor)
print(model.n_estimators_)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, early stopping is determined by checking
the last 10 entries of the &lt;code&gt;oob_improvement_&lt;/code&gt; attribute.
It contains the improvement in loss on the out-of-bag samples
relative to the previous iteration.
This requires setting &lt;code&gt;subsample&lt;/code&gt; to a value smaller 1, here 0.8.
Using this approach, training stopped after 114 iterations.&lt;/p&gt;
&lt;h2 id=&#34;improved-performance-of-survivaltree-and-randomsurvivalforest&#34;&gt;Improved Performance of SurvivalTree and RandomSurvivalForest&lt;/h2&gt;
&lt;p&gt;Another exciting feature of scikit-survival 0.21.0 is due to a re-write of
the training routine of &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/api/generated/sksurv.tree.SurvivalTree.html#sksurv.tree.SurvivalTree&#34; target=&#34;_blank&#34;&gt;SurvivalTree&lt;/a&gt;.
This results in roughly &lt;strong&gt;3x faster training times&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://k-d-w.org/blog/2023/06/scikit-survival-0.21.0-released/img/fit-times.svg&#34;
alt=&#34;Runtime comparison of fitting SurvivalTree.&#34;/&gt; &lt;figcaption&gt;
&lt;p&gt;Runtime comparison of fitting SurvivalTree.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The plot above compares the time required to fit a single SurvivalTree on data with
25 features and varying number of samples.
The performance difference becomes notable for data with 1000 samples and above.&lt;/p&gt;
&lt;p&gt;Note that this improvement also speeds-up fitting
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/api/generated/sksurv.ensemble.RandomSurvivalForest.html#sksurv.ensemble.RandomSurvivalForest&#34; target=&#34;_blank&#34;&gt;RandomSurvivalForest&lt;/a&gt;
and &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/api/generated/sksurv.ensemble.ExtraSurvivalTrees.html#sksurv.ensemble.ExtraSurvivalTrees&#34; target=&#34;_blank&#34;&gt;ExtraSurvivalTrees&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;improved-concordance-index&#34;&gt;Improved concordance index&lt;/h2&gt;
&lt;p&gt;Another performance improvement is due to &lt;a href=&#34;https://github.com/cpoerschke&#34; target=&#34;_blank&#34;&gt;Christine Poerschke&lt;/a&gt;
who significantly reduced the memory footprint of
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/api/generated/sksurv.metrics.concordance_index_censored.html#sksurv.metrics.concordance_index_censored&#34; target=&#34;_blank&#34;&gt;concordance_index_censored()&lt;/a&gt;.
With scikit-survival 0.21.0, memory usage &lt;strong&gt;scales linear, instead of quadratic&lt;/strong&gt;, in the number of samples, making performance evaluation on large datasets much more manageable.&lt;/p&gt;
&lt;p&gt;For a full list of changes in scikit-survival 0.21.0, please see the
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.21.0/release_notes.html#scikit-survival-0-21-0-2023-06-11&#34; target=&#34;_blank&#34;&gt;release notes&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;install&#34;&gt;Install&lt;/h2&gt;
&lt;p&gt;Pre-built conda packages are available for Linux, macOS (Intel), and Windows, either&lt;/p&gt;
&lt;p&gt;via pip:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or via conda&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; conda install -c sebp scikit-survival
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>scikit-survival 0.18.0 released</title><link>https://k-d-w.org/blog/2022/08/scikit-survival-0.18.0-released/</link><pubDate>Mon, 15 Aug 2022 17:18:26 +0200</pubDate><guid>https://k-d-w.org/blog/2022/08/scikit-survival-0.18.0-released/</guid><description>&lt;p&gt;I&amp;rsquo;m pleased to announce the release of
&lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.18.0,
which adds support for scikit-learn 1.1.&lt;/p&gt;
&lt;p&gt;In addition, this release adds the &lt;code&gt;return_array&lt;/code&gt; argument to all models providing
&lt;code&gt;predict_survival_function&lt;/code&gt; and &lt;code&gt;predict_cumulative_hazard_function&lt;/code&gt;.
That means you can now choose, whether you want to have the survival
(cumulative hazard function) automatically evaluated at the unique event
times. This is particular useful for plotting. Previously, you would have
to evaluate each survival function before plotting:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;estimator = CoxPHSurvivalAnalysis()
estimator.fit(X_train, y_train)
pred_surv = estimator.predict_survival_function(
X_test
)
times = pred_surv[0].x
for surv_func in pred_surv:
plt.step(times, surv_func(times), where=&amp;quot;post&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, you can pass &lt;code&gt;return_array=True&lt;/code&gt; and directly get probabilities
of the survival function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;estimator = CoxPHSurvivalAnalysis()
estimator.fit(X_train, y_train)
pred_surv_probs = estimator.predict_survival_function(
X_test, return_array=True
)
times = estimator.event_times_
for probs in pred_surv_probs:
plt.step(times, probs, where=&amp;quot;post&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, support for Python 3.7 has been dropped and the minimal required
version of the following dependencies are raised:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;numpy 1.17.3&lt;/li&gt;
&lt;li&gt;pandas 1.0.5&lt;/li&gt;
&lt;li&gt;scikit-learn 1.1.0&lt;/li&gt;
&lt;li&gt;scipy 1.3.2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a full list of changes in scikit-survival 0.18.0, please see the
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.18.0/release_notes.html#scikit-survival-0-18-0-2022-08-15&#34; target=&#34;_blank&#34;&gt;release notes&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;install&#34;&gt;Install&lt;/h2&gt;
&lt;p&gt;Pre-built conda packages are available for Linux, macOS (Intel), and Windows, either&lt;/p&gt;
&lt;p&gt;via pip:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or via conda&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; conda install -c sebp scikit-survival
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Using VS Code and Podman to Develop SYCL Applications With DPC&#43;&#43;&#39;s CUDA Backend</title><link>https://k-d-w.org/blog/2022/06/using-vs-code-and-podman-to-develop-sycl-applications-with-dpc-s-cuda-backend/</link><pubDate>Sat, 11 Jun 2022 14:33:13 +0200</pubDate><guid>https://k-d-w.org/blog/2022/06/using-vs-code-and-podman-to-develop-sycl-applications-with-dpc-s-cuda-backend/</guid><description>&lt;p&gt;I recently wanted to create a &lt;a href=&#34;https://code.visualstudio.com/docs/remote/containers&#34; target=&#34;_blank&#34;&gt;development container&lt;/a&gt;
for VS Code to develop applications using &lt;a href=&#34;https://sycl.tech/&#34; target=&#34;_blank&#34;&gt;SYCL&lt;/a&gt; based on the &lt;a href=&#34;https://developer.nvidia.com/cuda-toolkit&#34; target=&#34;_blank&#34;&gt;CUDA&lt;/a&gt;
backend of the &lt;a href=&#34;https://intel.github.io/llvm-docs/&#34; target=&#34;_blank&#34;&gt;oneAPI DPC++&lt;/a&gt; (Data Parallel C++) compiler.
As I&amp;rsquo;m running Fedora, it seemed natural to use &lt;a href=&#34;https://podman.io/&#34; target=&#34;_blank&#34;&gt;Podman&amp;rsquo;s&lt;/a&gt; rootless containers instead of Docker for this.
This turned out to be more challenging than expected, so I&amp;rsquo;m going to summarize my setup in this post.
I&amp;rsquo;m using &lt;a href=&#34;https://getfedora.org/&#34; target=&#34;_blank&#34;&gt;Fedora Linux&lt;/a&gt; 36 with Podman version 4.1.0.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Since the DPC++ is going to use CUDA behind the scene, you will need an NVIDIA GPU and the corresponding Kernel driver for it.
I&amp;rsquo;ve been using the &lt;a href=&#34;https://rpmfusion.org/Howto/NVIDIA&#34; target=&#34;_blank&#34;&gt;NVIDIA GPU driver&lt;/a&gt; from RPM Fusion.
Note that you do not have to install CUDA, it is part of the development container alongside the DPC++ compiler.&lt;/p&gt;
&lt;p&gt;Next, you require Podman, which on Fedora can be installed by executing&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo dnf install -y podman
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you require &lt;a href=&#34;https://code.visualstudio.com/docs/setup/linux#_rhel-fedora-and-centos-based-distributions&#34; target=&#34;_blank&#34;&gt;VS Code&lt;/a&gt;
and the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&#34; target=&#34;_blank&#34;&gt;Remote - Containers&lt;/a&gt;
extension. Just follow the instructions behind those links.&lt;/p&gt;
&lt;h2 id=&#34;installing-and-configuring-the-nvidia-container-toolkit&#34;&gt;Installing and Configuring the NVIDIA Container Toolkit&lt;/h2&gt;
&lt;p&gt;The default configuration of the &lt;a href=&#34;https://nvidia.github.io/nvidia-docker/&#34; target=&#34;_blank&#34;&gt;NVIDIA Container Toolkit&lt;/a&gt; does not work with Podman, so it needs to be adjusted.
Most steps are based on &lt;a href=&#34;https://www.redhat.com/en/blog/how-use-gpus-containers-bare-metal-rhel-8&#34; target=&#34;_blank&#34;&gt;this guide&lt;/a&gt;
by Red Hat, which I will repeat below.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add the repository:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -sL https://nvidia.github.io/nvidia-docker/rhel9.0/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you aren&amp;rsquo;t using Fedora 36, you might have to replace &lt;code&gt;rhel9.0&lt;/code&gt; with your distribution, see the &lt;a href=&#34;https://nvidia.github.io/nvidia-docker/#rhel-based-distributions&#34; target=&#34;_blank&#34;&gt;instructions&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, install the &lt;code&gt;nvidia-container-toolkit&lt;/code&gt; package.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dnf install -y nvidia-container-toolkit
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.redhat.com/en/blog/how-use-gpus-containers-bare-metal-rhel-8&#34; target=&#34;_blank&#34;&gt;Red Hat&amp;rsquo;s guide&lt;/a&gt; mentioned to configure two settings in &lt;code&gt;/etc/nvidia-container-runtime/config.toml&lt;/code&gt;.
But when using Podman with &lt;code&gt;--userns=keep-id&lt;/code&gt; to map the UID of the user running the container to the user running inside the container, you have to change a &lt;a href=&#34;https://github.com/NVIDIA/libnvidia-container/issues/115#issuecomment-721975656&#34; target=&#34;_blank&#34;&gt;third setting&lt;/a&gt;.
So open
&lt;code&gt;/etc/nvidia-container-runtime/config.toml&lt;/code&gt; with&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo -e /etc/nvidia-container-runtime/config.toml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and change the following three lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#no-cgroups = false
no-cgroups = true
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;#ldconfig = &amp;quot;@/sbin/ldconfig&amp;quot;
ldconfig = &amp;quot;/sbin/ldconfig&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;#debug = &amp;quot;/var/log/nvidia-container-runtime.log&amp;quot;
debug = &amp;quot;~/.local/nvidia-container-runtime.log&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, you have to create a new SELinux policy to enable GPU access within the container:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;curl -sLO https://raw.githubusercontent.com/NVIDIA/dgx-selinux/master/bin/RHEL7/nvidia-container.pp
sudo semodule -i nvidia-container.pp
sudo nvidia-container-cli -k list | sudo restorecon -v -f -
sudo restorecon -Rv /dev
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, tell VS Code to use Podman instead of Docker by going to &lt;em&gt;User Settings&lt;/em&gt; ➞ &lt;em&gt;Extensions&lt;/em&gt; ➞ &lt;em&gt;Remote - Containers&lt;/em&gt;, and change
&lt;strong&gt;Remote › Containers: Docker Path&lt;/strong&gt; to &lt;code&gt;podman&lt;/code&gt;, as in the image below.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img src=&#34;https://github.com/sebp/vscode-sycl-dpcpp-cuda/raw/main/doc/settings-docker-path.png&#34;
alt=&#34;Docker Path setting&#34;/&gt; &lt;figcaption&gt;
&lt;p&gt;Replace &lt;code&gt;docker&lt;/code&gt; with &lt;code&gt;podman&lt;/code&gt;.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&#34;using-the-development-container&#34;&gt;Using the Development Container&lt;/h2&gt;
&lt;p&gt;I created an &lt;a href=&#34;https://github.com/sebp/vscode-sycl-dpcpp-cuda&#34; target=&#34;_blank&#34;&gt;example project&lt;/a&gt;
that is based on a &lt;a href=&#34;https://github.com/sebp/dpcpp-sycl-cuda-container&#34; target=&#34;_blank&#34;&gt;container&lt;/a&gt;
that provides&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CUDA 11.5&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.intel.com/content/www/us/en/developer/articles/release-notes/intel-oneapi-dpc-c-compiler-release-notes.html&#34; target=&#34;_blank&#34;&gt;oneAPI DPC++ 2022.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;cmake&lt;/li&gt;
&lt;li&gt;gdb&lt;/li&gt;
&lt;li&gt;git&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can install additional tools by editing the project&amp;rsquo;s &lt;a href=&#34;https://github.com/sebp/vscode-sycl-dpcpp-cuda/blob/main/.devcontainer/Dockerfile&#34; target=&#34;_blank&#34;&gt;Dockerfile&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To use the example project, clone it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;git clone https://github.com/sebp/vscode-sycl-dpcpp-cuda.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, open the &lt;code&gt;vscode-sycl-dpcpp-cuda&lt;/code&gt; directory with VS Code. At this point VS Code should recognize that the project contains
a development container and suggest reopening the project in the container&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&#34;https://github.com/sebp/vscode-sycl-dpcpp-cuda/raw/main/doc/reopen-in-container.png&#34;
alt=&#34;Reopen project in container&#34;/&gt; &lt;figcaption&gt;
&lt;p&gt;Click &lt;em&gt;Reopen in Container&lt;/em&gt;.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Initially, this step will take some time because the container&amp;rsquo;s image is downloaded and VS Code will install additional tools inside the container.
Subsequently, VS Code will reuse this container, and opening the project in the container will be quick.&lt;/p&gt;
&lt;p&gt;Once the project has been opened within the container, you can open the example SYCL application in the file &lt;code&gt;src/sycl-example.cpp&lt;/code&gt;.
The project is configured to use the DPC++ compiler with the CUDA backend by default.
Therefore, you just have to press &lt;code&gt;Ctrl+Shift+B&lt;/code&gt; to compile the example file.
Using the terminal, you can now execute the compiled program, which should print the GPU it is using and the numbers 0 to 31.&lt;/p&gt;
&lt;p&gt;Alternatively, you can compile and directly run the program by executing the &lt;em&gt;Test&lt;/em&gt; task by opening
the Command Palette (&lt;code&gt;F1&lt;/code&gt;, &lt;code&gt;Ctrl+Shift+P&lt;/code&gt;) and searching for &lt;strong&gt;Run Test Task&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;While the journey to use a rootless Podman container with access
to the GPU with VS Code was rather cumbersome, I hope this guide
will make it less painful for others.
The &lt;a href=&#34;https://github.com/sebp/vscode-sycl-dpcpp-cuda&#34; target=&#34;_blank&#34;&gt;example project&lt;/a&gt;
should provide a good reference for a &lt;code&gt;devcontainer.json&lt;/code&gt; to use rootless
Podman containers with GPU access.
If you aren&amp;rsquo;t interested in SYCL or DPC++, you can replace the exising Dockerfile.
There are two steps that are essential for this to work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;vscode&lt;/code&gt; user &lt;a href=&#34;https://github.com/sebp/dpcpp-sycl-cuda-container/blob/main/Dockerfile#L46-L50&#34; target=&#34;_blank&#34;&gt;inside the container&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Make sure you &lt;a href=&#34;https://github.com/sebp/vscode-sycl-dpcpp-cuda/blob/main/.devcontainer/Dockerfile#L14-L18&#34; target=&#34;_blank&#34;&gt;create certain directories&lt;/a&gt;
that VS Code (inside the container) will require access to.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Otherwise, you will encounter various permission denied errors.&lt;/p&gt;</description></item><item><title>scikit-survival 0.17.2 released</title><link>https://k-d-w.org/blog/2022/04/scikit-survival-0.17.2-released/</link><pubDate>Mon, 25 Apr 2022 20:14:19 +0200</pubDate><guid>https://k-d-w.org/blog/2022/04/scikit-survival-0.17.2-released/</guid><description>&lt;p&gt;I&amp;rsquo;m pleased to announce the release of
&lt;a href=&#34;https://scikit-survival.readthedocs.io/&#34; target=&#34;_blank&#34;&gt;scikit-survival&lt;/a&gt; 0.17.2.
This release fixes several small issues with packaging scikit-survival
and the &lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.17.2/&#34; target=&#34;_blank&#34;&gt;documentation&lt;/a&gt;.
For a full list of changes in scikit-survival 0.17.2, please see the
&lt;a href=&#34;https://scikit-survival.readthedocs.io/en/v0.17.2/release_notes.html#scikit-survival-0-17-2-2022-04-24&#34; target=&#34;_blank&#34;&gt;release notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most notably, binary wheels are now available for Linux, Windows, and macOS (Intel).
This has been possible thanks to the &lt;a href=&#34;https://cibuildwheel.readthedocs.org/&#34; target=&#34;_blank&#34;&gt;cibuildwheel&lt;/a&gt;
build tool, which makes it incredible easy to use GitHub Actions for building
those wheels for multiple versions of Python.
Therefore, you can now use &lt;code&gt;pip&lt;/code&gt; without building everything from source by
simply running&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;pip install scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As before, pre-built conda packages are available too, by running&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; conda install -c sebp scikit-survival
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;support-for-apple-silicon-m1&#34;&gt;Support for Apple Silicon M1&lt;/h2&gt;
&lt;p&gt;Currently, there are no pre-built packages for Mac with Apple Silicon M1
hardware (also known as &lt;code&gt;macos/arm64&lt;/code&gt;).
There are two main reasons for that. The biggest problem is the lack of
CI servers that run on Apple Silicon M1. This makes it difficult to
systematically test scikit-survival on such hardware.
Second, some of scikit-survival&amp;rsquo;s dependencies do not offer wheels
for &lt;code&gt;macos/arm64&lt;/code&gt; yet, namely &lt;a href=&#34;https://pypi.org/project/ecos/&#34; target=&#34;_blank&#34;&gt;ecos&lt;/a&gt;
and &lt;a href=&#34;https://pypi.org/project/osqp/&#34; target=&#34;_blank&#34;&gt;osqp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, &lt;a href=&#34;conda-forge.org&#34; target=&#34;_blank&#34;&gt;conda-forge&lt;/a&gt; figured out a way to
cross-compile packages, and do offer
&lt;a href=&#34;https://anaconda.org/conda-forge/scikit-survival&#34; target=&#34;_blank&#34;&gt;scikit-survival for macos/arm64&lt;/a&gt;.
I tried to adapt their conda recipe, but failed to achieve a working
cross-compilation process so far.
Cross-compiling with &lt;code&gt;cibuildwheel&lt;/code&gt; would be an alternative, but currently
doesn&amp;rsquo;t make much sense if run-time dependencies &lt;code&gt;ecos&lt;/code&gt; and &lt;code&gt;osqp&lt;/code&gt; do not
provide wheels for &lt;code&gt;macos/arm64&lt;/code&gt;.
I hope these issues can be resolved in the near future.&lt;/p&gt;</description></item></channel></rss>