Repository: pandas-dev/pandas (Python)

Issue:
BUG: inconsistent spacing in df.info()
- [x] I have checked that this issue has not already been reported.

- [x] I have confirmed this bug exists on the latest version of pandas.

- [x] (optional) I have confirmed this bug exists on the master branch of pandas.

---

**Note**: Please read [this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) detailing how to provide the necessary information for us to reproduce your bug.

#### Code Sample, a copy-pastable example

See that the spacing between all columns is 2 spaces. Even if there is a long string in Non-Null Count column.
```python
df = pd.DataFrame({'long long column': np.random.rand(1000000)})
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 1 columns):
 #   Column            Non-Null Count    Dtype
---  ------            --------------    -----
 0   long long column  1000000 non-null  float64
dtypes: float64(1)
memory usage: 7.6 MB
```

Note that there is only one space between # and Column columns when the number of columns is over 1000 (last line of tail).

```python
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(3, 10001))
with open('out.txt', 'w') as buf:
    df.info(verbose=True, buf=buf)
```

```bash
$ tail out.txt
 9993  9993    float64
 9994  9994    float64
 9995  9995    float64
 9996  9996    float64
 9997  9997    float64
 9998  9998    float64
 9999  9999    float64
 10000 10000   float64
dtypes: float64(10001)
memory usage: 234.5 KB
```
#### Problem description

I find inconsistent behavior in spacing between columns in the output ``df.info()`` when dealing with dataframes having over 1000 columns.

#### Expected Output

Expect to have two spaces distance between all columns regardless of the col widths.


#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit           : f6ccbbc1ee852754a268c46454803bc9a3f9aebf
python           : 3.7.7.final.0
python-bits      : 64
OS               : Linux
OS-release       : 4.19.76-linuxkit
Version          : #1 SMP Tue May 26 11:42:35 UTC 2020
machine          : x86_64
processor        : 
byteorder        : little
LC_ALL           : C.UTF-8
LANG             : C.UTF-8
LOCALE           : en_US.UTF-8

pandas           : 1.2.0.dev0+559.gf6ccbbc1e.dirty
numpy            : 1.18.5
pytz             : 2020.1
dateutil         : 2.8.1
pip              : 20.2.1
setuptools       : 45.2.0.post20200210
Cython           : 0.29.21
pytest           : 6.0.1
hypothesis       : 5.23.11
sphinx           : 3.1.1
blosc            : None
feather          : None
xlsxwriter       : 1.3.1
lxml.etree       : 4.4.1
html5lib         : 1.1
pymysql          : None
psycopg2         : None
jinja2           : 2.11.2
IPython          : 7.17.0
pandas_datareader: None
bs4              : 4.9.1
bottleneck       : 1.3.2
fsspec           : 0.8.0
fastparquet      : 0.4.1
gcsfs            : 0.6.2
matplotlib       : 3.2.1
numexpr          : 2.7.1
odfpy            : None
openpyxl         : 3.0.4
pandas_gbq       : None
pyarrow          : 0.16.0
pytables         : None
pyxlsb           : None
s3fs             : 0.4.2
scipy            : 1.5.2
sqlalchemy       : 1.3.18
tables           : 3.6.1
tabulate         : 0.8.7
xarray           : 0.16.0
xlrd             : 1.2.0
xlwt             : 1.3.0
numba            : 0.50.1
</details>



Discussion:

Patch:
diff --git a/pandas/io/formats/info.py b/pandas/io/formats/info.py
index e8e41d4325103..9812ca39e2656 100644
--- a/pandas/io/formats/info.py
+++ b/pandas/io/formats/info.py
@@ -288,7 +288,11 @@ def _verbose_repr(
         len_column = len(pprint_thing(column_head))
         space = max(max_col, len_column) + col_space
 
-        max_id = len(pprint_thing(col_count))
+        # GH #36765
+        # add one space in max_id because there is a one-space padding
+        # in front of the number
+        # this allows maintain two spaces gap between columns
+        max_id = len(pprint_thing(col_count)) + 1
         len_id = len(pprint_thing(id_head))
         space_num = max(max_id, len_id) + col_space
 


Test patch:
diff --git a/pandas/tests/io/formats/test_info.py b/pandas/tests/io/formats/test_info.py
index 7000daeb9b575..d98530b5435e7 100644
--- a/pandas/tests/io/formats/test_info.py
+++ b/pandas/tests/io/formats/test_info.py
@@ -87,7 +87,7 @@ def test_info_verbose():
     frame.info(verbose=True, buf=buf)
 
     res = buf.getvalue()
-    header = " #    Column  Dtype  \n---   ------  -----  "
+    header = " #     Column  Dtype  \n---    ------  -----  "
     assert header in res
 
     frame.info(verbose=True, buf=buf)
@@ -101,6 +101,64 @@ def test_info_verbose():
             assert line.startswith(line_nr)
 
 
+@pytest.mark.parametrize(
+    "size, header_exp, separator_exp, first_line_exp, last_line_exp",
+    [
+        (
+            4,
+            " #   Column  Non-Null Count  Dtype  ",
+            "---  ------  --------------  -----  ",
+            " 0   0       3 non-null      float64",
+            " 3   3       3 non-null      float64",
+        ),
+        (
+            11,
+            " #   Column  Non-Null Count  Dtype  ",
+            "---  ------  --------------  -----  ",
+            " 0   0       3 non-null      float64",
+            " 10  10      3 non-null      float64",
+        ),
+        (
+            101,
+            " #    Column  Non-Null Count  Dtype  ",
+            "---   ------  --------------  -----  ",
+            " 0    0       3 non-null      float64",
+            " 100  100     3 non-null      float64",
+        ),
+        (
+            1001,
+            " #     Column  Non-Null Count  Dtype  ",
+            "---    ------  --------------  -----  ",
+            " 0     0       3 non-null      float64",
+            " 1000  1000    3 non-null      float64",
+        ),
+        (
+            10001,
+            " #      Column  Non-Null Count  Dtype  ",
+            "---     ------  --------------  -----  ",
+            " 0      0       3 non-null      float64",
+            " 10000  10000   3 non-null      float64",
+        ),
+    ],
+)
+def test_info_verbose_with_counts_spacing(
+    size, header_exp, separator_exp, first_line_exp, last_line_exp
+):
+    """Test header column, spacer, first line and last line in verbose mode."""
+    frame = DataFrame(np.random.randn(3, size))
+    buf = StringIO()
+    frame.info(verbose=True, null_counts=True, buf=buf)
+    all_lines = buf.getvalue().splitlines()
+    # Here table would contain only header, separator and table lines
+    # dframe repr, index summary, memory usage and dtypes are excluded
+    table = all_lines[3:-2]
+    header, separator, first_line, *rest, last_line = table
+    assert header == header_exp
+    assert separator == separator_exp
+    assert first_line == first_line_exp
+    assert last_line == last_line_exp
+
+
 def test_info_memory():
     # https://github.com/pandas-dev/pandas/issues/21056
     df = DataFrame({"a": Series([1, 2], dtype="i8")})