Repository: statsmodels/statsmodels

Issue:
special Latex characters are not escaped in some table cells with Summary.as.latex()
With statsmodels 0.9.0:

Modifying the example from #2862:

```python
import numpy as np
import statsmodels.api as sm
import statsmodels.formula.api as smf

dat = sm.datasets.get_rdataset("Guerry", "HistData").data

# Rename 'Lottery' column to 'Lottery_'
dat.rename(columns = {'Lottery':'Lottery_'}, inplace = True)

results = smf.ols('Lottery_ ~ Crime_prop + np.log(Pop1831)', data=dat).fit()

print(results.summary().as_latex())
```

The printed Latex successfully escapes the underscore in `Crime_prop`:

`\textbf{Crime\_prop}     &       0.0029  &        0.001     &     3.887  &         0.000        &        0.001    &        0.004     \\`

 but does not escape the underscore in Lottery_:

`\textbf{Dep. Variable:}    &     Lottery_     & \textbf{  R-squared:         } &     0.351   \\`

I therefore can't produce a valid latex table when the dependent variable has an underscore.



Discussion:
this got lost without labels.
AFAICS, there is no recent PR that fixed this.

bump for 0.10

Resolving patch:
diff --git a/statsmodels/iolib/table.py b/statsmodels/iolib/table.py
--- a/statsmodels/iolib/table.py
+++ b/statsmodels/iolib/table.py
@@ -691,6 +691,16 @@ def alignment(self, output_format, **fmt_dict):
             raise ValueError('Unknown cell datatype: %s' % datatype)
         return align
 
+    @staticmethod
+    def _latex_escape(data, fmt, output_format):
+        if output_format != 'latex':
+            return data
+        if "replacements" in fmt:
+            if isinstance(data, str):
+                for repl in sorted(fmt["replacements"]):
+                    data = data.replace(repl, fmt["replacements"][repl])
+        return data
+
     def format(self, width, output_format='txt', **fmt_dict):
         """Return string.
         This is the default formatter for cells.
@@ -715,11 +725,10 @@ def format(self, width, output_format='txt', **fmt_dict):
         if isinstance(datatype, (int, long)):
             datatype = datatype % len(data_fmts)  # constrain to indexes
             content = data_fmts[datatype] % (data,)
+            if datatype == 0:
+                content = self._latex_escape(content, fmt, output_format)
         elif datatype in fmt:
-            if "replacements" in fmt:
-                if isinstance(data, str):
-                    for repl in sorted(fmt["replacements"]):
-                        data = data.replace(repl, fmt["replacements"][repl])
+            data = self._latex_escape(data, fmt, output_format)
 
             dfmt = fmt.get(datatype)
             try:


Test patch:
diff --git a/statsmodels/iolib/tests/test_summary.py b/statsmodels/iolib/tests/test_summary.py
--- a/statsmodels/iolib/tests/test_summary.py
+++ b/statsmodels/iolib/tests/test_summary.py
@@ -3,6 +3,24 @@
 
 '''
 from __future__ import print_function
+
+import numpy as np  # noqa: F401
+
+from statsmodels.datasets import macrodata
+from statsmodels.regression.linear_model import OLS
+
+
+def test_escaped_variable_name():
+    # Rename 'cpi' column to 'CPI_'
+    data = macrodata.load(True).data
+    data.rename(columns={'cpi': 'CPI_'}, inplace=True)
+
+    mod = OLS.from_formula('CPI_ ~ 1 + np.log(realgdp)', data=data)
+    res = mod.fit()
+    assert 'CPI\\_' in res.summary().as_latex()
+    assert 'CPI_' in res.summary().as_text()
+
+
 if __name__ == '__main__':
 
     from statsmodels.regression.tests.test_regression import TestOLS