Repository: dask/dask

Issue:
Regression in index when using `fastparquet`
**What happened**:
on `2.19.0+6.g7138f470f`

```
import pandas as pd
import dask.dataframe as dd

df = pd.DataFrame({'t': ['2017-02-03', '2017-03-03', '2017-01-01']})
df['t'] = pd.to_datetime(df.t, utc=True).dt.tz_convert('US/Eastern')
print(df)
print(df.index)
df.to_parquet('test.parquet', engine='fastparquet')
df = dd.read_parquet('test.parquet', engine='fastparquet').compute()
print(df)
print(df.index)
                          t
0 2017-02-02 19:00:00-05:00
1 2017-03-02 19:00:00-05:00
2 2016-12-31 19:00:00-05:00
RangeIndex(start=0, stop=3, step=1)
                                      t
7.342211e-309 2017-02-02 19:00:00-05:00
7.354163e-309 2017-03-02 19:00:00-05:00
7.328124e-309 2016-12-31 19:00:00-05:00
Float64Index([7.342210749717597e-309, 7.35416318582179e-309,
              7.32812395002337e-309],
             dtype='float64')
```

**What you expected to happen**:
on `2.19.0`

```
import pandas as pd
import dask.dataframe as dd

df = pd.DataFrame({'t': ['2017-02-03', '2017-03-03', '2017-01-01']})
df['t'] = pd.to_datetime(df.t, utc=True).dt.tz_convert('US/Eastern')
print(df)
print(df.index)
df.to_parquet('test.parquet', engine='fastparquet')
df = dd.read_parquet('test.parquet', engine='fastparquet').compute()
print(df)
print(df.index)
                          t
0 2017-02-02 19:00:00-05:00
1 2017-03-02 19:00:00-05:00
2 2016-12-31 19:00:00-05:00
RangeIndex(start=0, stop=3, step=1)
                          t
0 2017-02-02 19:00:00-05:00
1 2017-03-02 19:00:00-05:00
2 2016-12-31 19:00:00-05:00
RangeIndex(start=0, stop=3, step=1)
```

**Environment**:

- Dask version: 2.19.0+6.g7138f470f
- Python version: 3.8.2 conda-forge
- Operating System: OSX
- Install method (conda, pip, source): source



Discussion:
@rjzamora looks like this started with #6282 (which is a little odd, since this only impacts `fastparquet` and not `pyarrow`)
Oops - Sorry about that. I'll try to track down the problem and add test coverage for this.
Looks like we are failing to round-trip a RangIndex on the fastparquet side.

Another repro:
```python
import pandas as pd
import dask.dataframe as dd

df0 = pd.DataFrame(
    {'t': [1, 2, 3]},
    index=pd.RangeIndex(start=1, stop=4),
)
df0.to_parquet('test.parquet', engine='fastparquet')
df1 = dd.read_parquet('test.parquet', engine='fastparquet').compute()

dd.assert_eq(df0.index, df1.index)
```
Actually, it looks like we never supported round-tripping a `RangeIndex` with fastparquet. The reason [this](https://github.com/dask/dask/issues/6348#issue-646369692) example worked with 2.19.0 was that we didn't even try to preserve the index, and the default`RangeIndex` just happened to be correct.

When you try to round-trip a non-default `RangeIndex`, both versions fail.  @martindurant - Does fastparquet allow us to read  in a `RangeIndex` like so: `pf.read_row_group_file(rg_piece, columns, categories, index=index)` (where `index` specifies a `RangeIndex` column)?  If not, we may need to use the "pandas metadata" to accomplish this.

Resolving patch:
diff --git a/dask/dataframe/io/parquet/fastparquet.py b/dask/dataframe/io/parquet/fastparquet.py
--- a/dask/dataframe/io/parquet/fastparquet.py
+++ b/dask/dataframe/io/parquet/fastparquet.py
@@ -446,8 +446,19 @@ def read_metadata(
     def read_partition(
         cls, fs, piece, columns, index, categories=(), pf=None, **kwargs
     ):
+
+        null_index_name = False
         if isinstance(index, list):
-            index = ["__index_level_0__" if i is None else i for i in index]
+            if index == [None]:
+                # Handling a None-labeled index...
+                # The pandas metadata told us to read in an index
+                # labeled `None`. If this corresponds to a `RangeIndex`,
+                # fastparquet will need use the pandas metadata to
+                # construct the index. Otherwise, the index will correspond
+                # to a column named "__index_level_0__".  We will need to
+                # check the `ParquetFile` object for this column below.
+                index = []
+                null_index_name = True
             columns += index
 
         if pf is None:
@@ -461,6 +472,10 @@ def read_partition(
             pf.file_scheme = scheme
             pf.cats = paths_to_cats(fns, scheme)
             pf.fn = base
+            if null_index_name and "__index_level_0__" in pf.columns:
+                # See "Handling a None-labeled index" comment above
+                index = ["__index_level_0__"]
+                columns += index
             return pf.to_pandas(columns, categories, index=index)
         else:
             if isinstance(pf, tuple):
@@ -473,7 +488,14 @@ def read_partition(
                 pf._dtypes = lambda *args: pf.dtypes  # ugly patch, could be fixed
                 pf.fmd.row_groups = None
             rg_piece = pf.row_groups[piece]
-            pf.fmd.key_value_metadata = None
+            if null_index_name:
+                if "__index_level_0__" in pf.columns:
+                    # See "Handling a None-labeled index" comment above
+                    index = ["__index_level_0__"]
+                    columns += index
+                    pf.fmd.key_value_metadata = None
+            else:
+                pf.fmd.key_value_metadata = None
             return pf.read_row_group_file(
                 rg_piece, columns, categories, index=index, **kwargs.get("read", {})
             )


Test patch:
diff --git a/dask/dataframe/io/tests/test_parquet.py b/dask/dataframe/io/tests/test_parquet.py
--- a/dask/dataframe/io/tests/test_parquet.py
+++ b/dask/dataframe/io/tests/test_parquet.py
@@ -2554,6 +2554,17 @@ def test_from_pandas_preserve_none_index(tmpdir, engine):
     assert_eq(expect, got)
 
 
+@write_read_engines()
+def test_from_pandas_preserve_none_rangeindex(tmpdir, write_engine, read_engine):
+    # See GitHub Issue#6348
+    fn = str(tmpdir.join("test.parquet"))
+    df0 = pd.DataFrame({"t": [1, 2, 3]}, index=pd.RangeIndex(start=1, stop=4))
+    df0.to_parquet(fn, engine=write_engine)
+
+    df1 = dd.read_parquet(fn, engine=read_engine)
+    assert_eq(df0, df1.compute())
+
+
 def test_illegal_column_name(tmpdir, engine):
     # Make sure user is prevented from preserving a "None" index
     # name if there is already a column using the special `null_name`