---
jupyter:
  jupytext:
    notebook_metadata_filter: all,-language_info
    split_at_heading: true
    text_representation:
      extension: .Rmd
      format_name: rmarkdown
      format_version: '1.2'
      jupytext_version: 1.16.0
  kernelspec:
    display_name: Python 3 (ipykernel)
    language: python
    name: python3
  orphan: true
---

# Statsmodels where columns have spaces

```{python tags=c("hide-cell")}
import numpy as np
import pandas as pd
# Safe setting for Pandas.  Needs Pandas version >= 1.5.
pd.set_option('mode.copy_on_write', True)

import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')

np.set_printoptions(suppress=True)

import statsmodels.formula.api as smf
```

Back at the [simple and multiple regression page](single_multiple), we introduced the [StatsModels](https://www.statsmodels.org) library.

As you may remember from that page, we were looking for the best
least-squares straight-line fit between two columns of values in
patients with chronic kidney disease:

```{python}
ckd = pd.read_csv('ckd.csv')
ckd.head()
```

In particular we were interested to predict the blood concentration of creatinine (a marker of kidney failure) from the blood concentration of urea (another marker of kidney failure), in patients with chronic kidney disease.

Notice that creatinine and urea columns are `"Serum Creatinine"`
and `"Blood Urea"` respectively.  Both columns have spaces in their names.

We first select our columns of interest, and restrict ourselves to rows for patients with chronic kidney disease (CKD):

```{python}
# Data frame restricted to kidney patients and columns of interest.
ckdp = ckd.loc[
    ckd['Class'] == 1,   # rows for CKD
    ['Serum Creatinine', 'Blood Urea']]  # columns of interest
ckdp.head()
```

```{python}
ckdp.plot.scatter('Blood Urea', 'Serum Creatinine')
```

In the single and multiple regression page, we started by renaming
the columns, where the new names did not have spaces.

```{python}
ckdp_renamed = ckdp.copy()
# Rename the columns to names without spaces.
ckdp_renamed.columns = ['Creatinine', 'Urea']
ckdp_renamed.head()
```

We then ran a simple regression model in Statsmodels, to find the least-squares straight line.

```{python}
simple_model = smf.ols(formula="Creatinine ~ Urea", data=ckdp_renamed)
simple_fit = simple_model.fit()
simple_fit.summary()
```

But, if we wanted to use the original column names, we would have to do some
extra work to make Statsmodels accept column names with spaces.  And in fact we
have to do the same thing if there are special characters, which, like the
spaces would make the column names invalid as [variable
names](../code-basics/Names.Rmd).

For example, let's say we were using the DataFrame `ckdp` with the original
column names.  We could try this:

```{python tags=c("raises-exception")}
# This generates an error, because the Statsmodels formula interface
# needs column names that work as variable names.
another_model = smf.ols(formula="Serum Creatinine ~ Blood Urea",
                        data=ckdp)
```

The solution is to use the `Q()`
([Quote](https://patsy.readthedocs.io/en/latest/builtins-reference.html#patsy.builtins.Q))
function in your formula.  It tells Statsmodels that you mean the words 'Serum'
and 'Creatinine' to be one thing: 'Serum Creatinine' - the name of the column.

```{python}
another_model = smf.ols(formula="Q('Serum Creatinine') ~ Q('Blood Urea')",
                        data=ckdp)
another_fit = another_model.fit()
another_fit.summary()
```
