Repository: pandas-dev/pandas

Issue:
BUG: to_html misses truncation indicators (...) when index=False and max_cols < len(columns)
#### Code Sample

```python
np.random.seed(seed=0)
df = pd.DataFrame(np.random.randn(2,5))
df.to_html(max_cols=4, index=False)
```

#### Problem description

similar to #15019 with `max_cols` instead of `max_rows`

#### Output

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th>0</th>
      <th>1</th>
      <th>3</th>
      <th>...</th>
      <th>4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1.764052</td>
      <td>0.400157</td>
      <td>2.240893</td>
      <td>1.867558</td>
    </tr>
    <tr>
      <td>-0.977278</td>
      <td>0.950088</td>
      <td>-0.103219</td>
      <td>0.410599</td>
    </tr>
  </tbody>
</table>

#### Expected Output

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th>0</th>
      <th>1</th>
      <th>...</th>
      <th>3</th>
      <th>4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1.764052</td>
      <td>0.400157</td>
      <td>...</td>
      <td>2.240893</td>
      <td>1.867558</td>
    </tr>
    <tr>
      <td>-0.977278</td>
      <td>0.950088</td>
      <td>...</td>
      <td>-0.103219</td>
      <td>0.410599</td>
    </tr>
  </tbody>
</table>

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.6.5.final.0
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 58 Stepping 9, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None

pandas: 0.23.0
pytest: 3.5.1
pip: 10.0.1
setuptools: 39.1.0
Cython: 0.28.2
numpy: 1.14.3
scipy: 1.1.0
pyarrow: None
xarray: None
IPython: 6.4.0
sphinx: 1.7.4
patsy: 0.5.0
dateutil: 2.7.3
pytz: 2018.4
blosc: None
bottleneck: 1.2.1
tables: 3.4.3
numexpr: 2.6.5
feather: None
matplotlib: 2.2.2
openpyxl: 2.5.3
xlrd: 1.1.0
xlwt: 1.3.0
xlsxwriter: 1.0.4
lxml: 4.2.1
bs4: 4.6.0
html5lib: 1.0.1
sqlalchemy: 1.2.7
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None

</details>



Discussion:

Resolving patch:
diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py
--- a/pandas/io/formats/html.py
+++ b/pandas/io/formats/html.py
@@ -305,6 +305,8 @@ def _column_header():
             align = self.fmt.justify
 
             if truncate_h:
+                if not self.fmt.index:
+                    row_levels = 0
                 ins_col = row_levels + self.fmt.tr_col_num
                 col_row.insert(ins_col, '...')
 
@@ -336,15 +338,10 @@ def _write_body(self, indent):
             fmt_values[i] = self.fmt._format_col(i)
 
         # write values
-        if self.fmt.index:
-            if isinstance(self.frame.index, ABCMultiIndex):
-                self._write_hierarchical_rows(fmt_values, indent)
-            else:
-                self._write_regular_rows(fmt_values, indent)
+        if self.fmt.index and isinstance(self.frame.index, ABCMultiIndex):
+            self._write_hierarchical_rows(fmt_values, indent)
         else:
-            for i in range(min(len(self.frame), self.max_rows)):
-                row = [fmt_values[j][i] for j in range(len(self.columns))]
-                self.write_tr(row, indent, self.indent_delta, tags=None)
+            self._write_regular_rows(fmt_values, indent)
 
         indent -= self.indent_delta
         self.write('</tbody>', indent)
@@ -358,11 +355,16 @@ def _write_regular_rows(self, fmt_values, indent):
 
         ncols = len(self.fmt.tr_frame.columns)
         nrows = len(self.fmt.tr_frame)
-        fmt = self.fmt._get_formatter('__index__')
-        if fmt is not None:
-            index_values = self.fmt.tr_frame.index.map(fmt)
+
+        if self.fmt.index:
+            fmt = self.fmt._get_formatter('__index__')
+            if fmt is not None:
+                index_values = self.fmt.tr_frame.index.map(fmt)
+            else:
+                index_values = self.fmt.tr_frame.index.format()
+            row_levels = 1
         else:
-            index_values = self.fmt.tr_frame.index.format()
+            row_levels = 0
 
         row = []
         for i in range(nrows):
@@ -370,17 +372,18 @@ def _write_regular_rows(self, fmt_values, indent):
             if truncate_v and i == (self.fmt.tr_row_num):
                 str_sep_row = ['...'] * len(row)
                 self.write_tr(str_sep_row, indent, self.indent_delta,
-                              tags=None, nindex_levels=1)
+                              tags=None, nindex_levels=row_levels)
 
             row = []
-            row.append(index_values[i])
+            if self.fmt.index:
+                row.append(index_values[i])
             row.extend(fmt_values[j][i] for j in range(ncols))
 
             if truncate_h:
-                dot_col_ix = self.fmt.tr_col_num + 1
+                dot_col_ix = self.fmt.tr_col_num + row_levels
                 row.insert(dot_col_ix, '...')
             self.write_tr(row, indent, self.indent_delta, tags=None,
-                          nindex_levels=1)
+                          nindex_levels=row_levels)
 
     def _write_hierarchical_rows(self, fmt_values, indent):
         template = 'rowspan="{span}" valign="top"'


Test patch:
diff --git a/pandas/tests/io/formats/data/gh15019_expected_output.html b/pandas/tests/io/formats/data/gh15019_expected_output.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/gh15019_expected_output.html
@@ -0,0 +1,30 @@
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th>0</th>
+      <th>1</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>1.764052</td>
+      <td>0.400157</td>
+    </tr>
+    <tr>
+      <td>0.978738</td>
+      <td>2.240893</td>
+    </tr>
+    <tr>
+      <td>...</td>
+      <td>...</td>
+    </tr>
+    <tr>
+      <td>0.950088</td>
+      <td>-0.151357</td>
+    </tr>
+    <tr>
+      <td>-0.103219</td>
+      <td>0.410599</td>
+    </tr>
+  </tbody>
+</table>
diff --git a/pandas/tests/io/formats/data/gh22783_expected_output.html b/pandas/tests/io/formats/data/gh22783_expected_output.html
new file mode 100644
--- /dev/null
+++ b/pandas/tests/io/formats/data/gh22783_expected_output.html
@@ -0,0 +1,27 @@
+<table border="1" class="dataframe">
+  <thead>
+    <tr style="text-align: right;">
+      <th>0</th>
+      <th>1</th>
+      <th>...</th>
+      <th>3</th>
+      <th>4</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>1.764052</td>
+      <td>0.400157</td>
+      <td>...</td>
+      <td>2.240893</td>
+      <td>1.867558</td>
+    </tr>
+    <tr>
+      <td>-0.977278</td>
+      <td>0.950088</td>
+      <td>...</td>
+      <td>-0.103219</td>
+      <td>0.410599</td>
+    </tr>
+  </tbody>
+</table>
diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py
--- a/pandas/tests/io/formats/test_to_html.py
+++ b/pandas/tests/io/formats/test_to_html.py
@@ -22,6 +22,28 @@
     pass
 
 
+def expected_html(datapath, name):
+    """
+    Read HTML file from formats data directory.
+
+    Parameters
+    ----------
+    datapath : pytest fixture
+        The datapath fixture injected into a test by pytest.
+    name : str
+        The name of the HTML file without the suffix.
+
+    Returns
+    -------
+    str : contents of HTML file.
+    """
+    filename = '.'.join([name, 'html'])
+    filepath = datapath('io', 'formats', 'data', filename)
+    with open(filepath) as f:
+        html = f.read()
+    return html.rstrip()
+
+
 class TestToHTML(object):
 
     def test_to_html_with_col_space(self):
@@ -1881,6 +1903,29 @@ def test_to_html_multiindex_max_cols(self):
         </table>""")
         assert result == expected
 
+    @pytest.mark.parametrize('index', [False, 0])
+    def test_to_html_truncation_index_false_max_rows(self, datapath, index):
+        # GH 15019
+        data = [[1.764052, 0.400157],
+                [0.978738, 2.240893],
+                [1.867558, -0.977278],
+                [0.950088, -0.151357],
+                [-0.103219, 0.410599]]
+        df = pd.DataFrame(data)
+        result = df.to_html(max_rows=4, index=index)
+        expected = expected_html(datapath, 'gh15019_expected_output')
+        assert result == expected
+
+    @pytest.mark.parametrize('index', [False, 0])
+    def test_to_html_truncation_index_false_max_cols(self, datapath, index):
+        # GH 22783
+        data = [[1.764052, 0.400157, 0.978738, 2.240893, 1.867558],
+                [-0.977278, 0.950088, -0.151357, -0.103219, 0.410599]]
+        df = pd.DataFrame(data)
+        result = df.to_html(max_cols=4, index=index)
+        expected = expected_html(datapath, 'gh22783_expected_output')
+        assert result == expected
+
     def test_to_html_notebook_has_style(self):
         df = pd.DataFrame({"A": [1, 2, 3]})
         result = df.to_html(notebook=True)