Repository: dask/dask

Issue:
Dask has no 'read_table()` function? 
I am trying to import the following file with dask:

```
import dask.dataframe as dd
file = dd.read_table("filename...")
```

Here's the error I receive

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'dask.dataframe' has no attribute 'read_table'
```



Discussion:
Does dd.read_csv work for you?

On Mon, Oct 17, 2016 at 6:37 PM, evanbiederstedt notifications@github.com
wrote:

> I am trying to import the following file with dask:
> 
> import dask.dataframe as dd
> file = dd.read_table("filename...")
> 
> Here's the error I receive
> 
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> AttributeError: module 'dask.dataframe' has no attribute 'read_table'
> 
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> https://github.com/dask/dask/issues/1673, or mute the thread
> https://github.com/notifications/unsubscribe-auth/AASszPt-TblJyw8XNtX7EQFyfEgTc8qMks5q0_iZgaJpZM4KZL5T
> .

@mrocklin Yes, `dd.read_csv()` works, but not for this dataset. It is possible for files this size to convert to csv via pandas first, but that may not be the case in the future. 

This should be fairly easy support with a refactor of dask's `read_csv`. Under the hood, `read_table` and `read_csv` are implemented by much of the same functionality in pandas. Working on it now.

@jcrist Thanks! That would be awesome. 

I just converted several TB of files into csv format in order to use `dd.read_csv`. That feels silly.


Resolving patch:
diff --git a/dask/dataframe/__init__.py b/dask/dataframe/__init__.py
--- a/dask/dataframe/__init__.py
+++ b/dask/dataframe/__init__.py
@@ -4,7 +4,8 @@
                    repartition)
 from .io import (from_array, from_pandas, from_bcolz,
                  from_dask_array, from_castra, read_hdf,
-                 from_delayed, read_csv, to_csv, demo)
+                 from_delayed, read_csv, to_csv, read_table,
+                 demo)
 from .optimize import optimize
 from .multi import merge, concat, melt
 from .rolling import (rolling_count, rolling_sum, rolling_mean, rolling_median,
diff --git a/dask/dataframe/io/__init__.py b/dask/dataframe/io/__init__.py
--- a/dask/dataframe/io/__init__.py
+++ b/dask/dataframe/io/__init__.py
@@ -3,6 +3,6 @@
 from .io import (from_array, from_bcolz, from_array, from_bcolz,
                  from_pandas, from_dask_array, from_castra, to_castra,
                  from_delayed, dataframe_from_ctable, to_bag)
-from .csv import read_csv, to_csv
+from .csv import read_csv, to_csv, read_table
 from .hdf import read_hdf, to_hdf
 from . import demo
diff --git a/dask/dataframe/io/csv.py b/dask/dataframe/io/csv.py
--- a/dask/dataframe/io/csv.py
+++ b/dask/dataframe/io/csv.py
@@ -24,30 +24,33 @@
 delayed = delayed(pure=True)
 
 
-def bytes_read_csv(b, header, kwargs, dtypes=None, columns=None,
-                   write_header=True, enforce=False):
+def pandas_read_text(reader, b, header, kwargs, dtypes=None, columns=None,
+                     write_header=True, enforce=False):
     """ Convert a block of bytes to a Pandas DataFrame
 
     Parameters
     ----------
-    b: bytestring
-        The content to be parsed with pandas.read_csv
-    header: bytestring
-        An optional header to prepend to b
-    kwargs: dict
-        A dictionary of keyword arguments to be passed to pandas.read_csv
-    dtypes: dict
+    reader : callable
+        ``pd.read_csv`` or ``pd.read_table``.
+    b : bytestring
+        The content to be parsed with ``reader``
+    header : bytestring
+        An optional header to prepend to ``b``
+    kwargs : dict
+        A dictionary of keyword arguments to be passed to ``reader``
+    dtypes : dict
         DTypes to assign to columns
 
-    See Also:
-        dask.dataframe.csv.read_csv_from_bytes
+    See Also
+    --------
+    dask.dataframe.csv.read_pandas_from_bytes
     """
     bio = BytesIO()
     if write_header and not b.startswith(header.rstrip()):
         bio.write(header)
     bio.write(b)
     bio.seek(0)
-    df = pd.read_csv(bio, **kwargs)
+    df = reader(bio, **kwargs)
     if dtypes:
         coerce_dtypes(df, dtypes)
 
@@ -72,13 +75,14 @@ def coerce_dtypes(df, dtypes):
                     np.issubdtype(dtypes[c], np.integer)):
                 if (df[c] % 1).any():
                     msg = ("Runtime type mismatch. "
-                           "Add {'%s': float} to dtype= keyword in read_csv")
+                           "Add {'%s': float} to dtype= keyword in "
+                           "read_csv/read_table")
                     raise TypeError(msg % c)
             df[c] = df[c].astype(dtypes[c])
 
 
-def read_csv_from_bytes(block_lists, header, head, kwargs, collection=True,
-                        enforce=False):
+def text_blocks_to_pandas(reader, block_lists, header, head, kwargs,
+                          collection=True, enforce=False):
     """ Convert blocks of bytes to a dask.dataframe or other high-level object
 
     This accepts a list of lists of values of bytes where each list corresponds
@@ -87,16 +91,18 @@ def read_csv_from_bytes(block_lists, header, head, kwargs, collection=True,
 
     Parameters
     ----------
-    block_lists: list of lists of delayed values of bytes
+    reader : callable
+        ``pd.read_csv`` or ``pd.read_table``.
+    block_lists : list of lists of delayed values of bytes
         The lists of bytestrings where each list corresponds to one logical file
-    header: bytestring
+    header : bytestring
         The header, found at the front of the first file, to be prepended to
         all blocks
-    head: pd.DataFrame
+    head : pd.DataFrame
         An example Pandas DataFrame to be used for metadata.
         Can be ``None`` if ``collection==False``
-    kwargs: dict
-        Keyword arguments to pass down to ``pd.read_csv``
+    kwargs : dict
+        Keyword arguments to pass down to ``reader``
     collection: boolean, optional (defaults to True)
 
     Returns
@@ -105,18 +111,19 @@ def read_csv_from_bytes(block_lists, header, head, kwargs, collection=True,
     """
     dtypes = head.dtypes.to_dict()
     columns = list(head.columns)
-    delayed_bytes_read_csv = delayed(bytes_read_csv)
+    delayed_pandas_read_text = delayed(pandas_read_text)
     dfs = []
     for blocks in block_lists:
         if not blocks:
             continue
-        df = delayed_bytes_read_csv(blocks[0], header, kwargs, dtypes,
-                                    columns, write_header=False,
-                                    enforce=enforce)
+        df = delayed_pandas_read_text(reader, blocks[0], header, kwargs,
+                                      dtypes, columns, write_header=False,
+                                      enforce=enforce)
         dfs.append(df)
         for b in blocks[1:]:
-            dfs.append(delayed_bytes_read_csv(b, header, kwargs, dtypes,
-                                              columns, enforce=enforce))
+            dfs.append(delayed_pandas_read_text(reader, b, header, kwargs,
+                                                dtypes, columns,
+                                                enforce=enforce))
 
     if collection:
         return from_delayed(dfs, head)
@@ -141,75 +148,33 @@ def auto_blocksize(total_memory, cpu_count):
     AUTO_BLOCKSIZE = 2**25
 
 
-def read_csv(urlpath, blocksize=AUTO_BLOCKSIZE, collection=True,
-             lineterminator=None, compression=None, sample=256000,
-             enforce=False, storage_options=None, **kwargs):
-    """ Read CSV files into a Dask.DataFrame
-
-    This parallelizes the ``pandas.read_csv`` file in the following ways:
-
-    1.  It supports loading many files at once using globstrings as follows:
-
-        >>> df = dd.read_csv('myfiles.*.csv')  # doctest: +SKIP
-
-    2.  In some cases it can break up large files as follows:
-
-        >>> df = dd.read_csv('largefile.csv', blocksize=25e6)  # 25MB chunks  # doctest: +SKIP
-
-    3.  You can read CSV files from external resources (e.g. S3, HDFS)
-        providing a URL:
-
-        >>> df = dd.read_csv('s3://bucket/myfiles.*.csv')  # doctest: +SKIP
-        >>> df = dd.read_csv('hdfs:///myfiles.*.csv')  # doctest: +SKIP
-        >>> df = dd.read_csv('hdfs://namenode.example.com/myfiles.*.csv')  # doctest: +SKIP
-
-    Internally dd.read_csv uses pandas.read_csv and so supports many of the
-    same keyword arguments with the same performance guarantees.
-
-    See the docstring for ``pandas.read_csv`` for more information on available
-    keyword arguments.
-
-    Note that this function may fail if a CSV file includes quoted strings that
-    contain the line terminator.
-
-    Parameters
-    ----------
-
-    urlpath: string
-        Absolute or relative filepath, URL (may include protocols like
-        ``s3://``), or globstring for CSV files.
-    blocksize: int or None
-        Number of bytes by which to cut up larger files. Default value is
-        computed based on available physical memory and the number of cores.
-        If ``None``, use a single block for each file.
-    collection: boolean
-        Return a dask.dataframe if True or list of dask.delayed objects if False
-    sample: int
-        Number of bytes to use when determining dtypes
-    storage_options: dict
-        Extra options that make sense to a particular storage connection, e.g.
-        host, port, username, password, etc.
-    **kwargs: dict
-        Options to pass down to ``pandas.read_csv``
-    """
+def read_pandas(reader, urlpath, blocksize=AUTO_BLOCKSIZE, collection=True,
+                lineterminator=None, compression=None, sample=256000,
+                enforce=False, storage_options=None, **kwargs):
+    reader_name = reader.__name__
     if lineterminator is not None and len(lineterminator) == 1:
         kwargs['lineterminator'] = lineterminator
     else:
         lineterminator = '\n'
     if 'index' in kwargs or 'index_col' in kwargs:
         raise ValueError("Keyword 'index' not supported "
-                         "dd.read_csv(...).set_index('my-index') instead")
+                         "dd.{0}(...).set_index('my-index') "
+                         "instead".format(reader_name))
     for kw in ['iterator', 'chunksize']:
         if kw in kwargs:
-            raise ValueError("%s not supported for dd.read_csv" % kw)
+            raise ValueError("{0} not supported for "
+                             "dd.{1}".format(kw, reader_name))
     if kwargs.get('nrows', None):
         raise ValueError("The 'nrows' keyword is not supported by "
-                         "`dd.read_csv`. To achieve the same behavior, it's "
-                         "recommended to use `dd.read_csv(...).head(n=nrows)`")
+                         "`dd.{0}`. To achieve the same behavior, it's "
+                         "recommended to use `dd.{0}(...)."
+                         "head(n=nrows)`".format(reader_name))
     if isinstance(kwargs.get('skiprows'), list):
-        raise TypeError("List of skiprows not supported for dd.read_csv")
+        raise TypeError("List of skiprows not supported for "
+                        "dd.{0}".format(reader_name))
     if isinstance(kwargs.get('header'), list):
-        raise TypeError("List of header rows not supported for dd.read_csv")
+        raise TypeError("List of header rows not supported for "
+                        "dd.{0}".format(reader_name))
 
     if blocksize and compression not in seekable_files:
         warn("Warning %s compression does not support breaking apart files\n"
@@ -236,12 +201,80 @@ def read_csv(urlpath, blocksize=AUTO_BLOCKSIZE, collection=True,
     else:
         header = sample.split(b_lineterminator)[0] + b_lineterminator
 
-    head = pd.read_csv(BytesIO(sample), **kwargs)
+    head = reader(BytesIO(sample), **kwargs)
 
-    df = read_csv_from_bytes(values, header, head, kwargs,
-                             collection=collection, enforce=enforce)
+    return text_blocks_to_pandas(reader, values, header, head, kwargs,
+                                 collection=collection, enforce=enforce)
 
-    return df
+
+READ_DOC_TEMPLATE = """
+Read {file_type} files into a Dask.DataFrame
+
+This parallelizes the ``pandas.{reader}`` file in the following ways:
+
+1.  It supports loading many files at once using globstrings as follows:
+
+    >>> df = dd.{reader}('myfiles.*.csv')  # doctest: +SKIP
+
+2.  In some cases it can break up large files as follows:
+
+    >>> df = dd.{reader}('largefile.csv', blocksize=25e6)  # 25MB chunks  # doctest: +SKIP
+
+3.  You can read CSV files from external resources (e.g. S3, HDFS)
+    providing a URL:
+
+    >>> df = dd.{reader}('s3://bucket/myfiles.*.csv')  # doctest: +SKIP
+    >>> df = dd.{reader}('hdfs:///myfiles.*.csv')  # doctest: +SKIP
+    >>> df = dd.{reader}('hdfs://namenode.example.com/myfiles.*.csv')  # doctest: +SKIP
+
+Internally ``dd.{reader}`` uses ``pandas.{reader}`` and so supports many
+of the same keyword arguments with the same performance guarantees.
+
+See the docstring for ``pandas.{reader}`` for more information on available
+keyword arguments.
+
+Note that this function may fail if a {file_type} file includes quoted strings
+that contain the line terminator.
+
+Parameters
+----------
+urlpath : string
+    Absolute or relative filepath, URL (may include protocols like
+    ``s3://``), or globstring for {file_type} files.
+blocksize : int or None
+    Number of bytes by which to cut up larger files. Default value is
+    computed based on available physical memory and the number of cores.
+    If ``None``, use a single block for each file.
+collection : boolean
+    Return a dask.dataframe if True or list of dask.delayed objects if False
+sample : int
+    Number of bytes to use when determining dtypes
+storage_options : dict
+    Extra options that make sense to a particular storage connection, e.g.
+    host, port, username, password, etc.
+**kwargs : dict
+    Options to pass down to ``pandas.{reader}``
+"""
+
+
+def make_reader(reader, reader_name, file_type):
+    def read(urlpath, blocksize=AUTO_BLOCKSIZE, collection=True,
+             lineterminator=None, compression=None, sample=256000,
+             enforce=False, storage_options=None, **kwargs):
+        return read_pandas(reader, urlpath, blocksize=blocksize,
+                           collection=collection,
+                           lineterminator=lineterminator,
+                           compression=compression, sample=sample,
+                           enforce=enforce, storage_options=storage_options,
+                           **kwargs)
+    read.__doc__ = READ_DOC_TEMPLATE.format(reader=reader_name,
+                                            file_type=file_type)
+    read.__name__ = reader_name
+    return read
+
+
+read_csv = make_reader(pd.read_csv, 'read_csv', 'CSV')
+read_table = make_reader(pd.read_table, 'read_table', 'delimited')
 
 
 @delayed


Test patch:
diff --git a/dask/dataframe/io/tests/test_csv.py b/dask/dataframe/io/tests/test_csv.py
--- a/dask/dataframe/io/tests/test_csv.py
+++ b/dask/dataframe/io/tests/test_csv.py
@@ -17,18 +17,20 @@
 from dask.async import get_sync
 
 import dask.dataframe as dd
-from dask.dataframe.io.csv import (read_csv_from_bytes, bytes_read_csv,
+from dask.dataframe.io.csv import (text_blocks_to_pandas, pandas_read_text,
                                    auto_blocksize)
 from dask.dataframe.utils import assert_eq
 from dask.bytes.core import read_bytes
 from dask.utils import filetexts, filetext, tmpfile, tmpdir
+from dask.bytes.compression import compress, files as cfiles, seekable_files
+fmt_bs = [(fmt, None) for fmt in cfiles] + [(fmt, 10) for fmt in seekable_files]
 
 
-########
-# CSVS #
-########
+def normalize_text(s):
+    return '\n'.join(map(str.strip, s.strip().split('\n')))
+
 
-text = """
+csv_text = """
 name,amount
 Alice,100
 Bob,-200
@@ -45,56 +47,219 @@
 Edith,600
 """.strip()
 
+tsv_text = csv_text.replace(',', '\t')
+
+tsv_text2 = """
+name   amount
+Alice    100
+Bob     -200
+Charlie  300
+Dennis   400
+Edith   -500
+Frank    600
+Alice    200
+Frank   -200
+Bob      600
+Alice    400
+Frank    200
+Alice    300
+Edith    600
+""".strip()
 
-def test_read_csv():
-    with filetext(text) as fn:
-        f = dd.read_csv(fn, blocksize=30, lineterminator=os.linesep)
-        assert list(f.columns) == ['name', 'amount']
-        result = f.compute(get=dask.get)
-        # index may be different
-        assert_eq(result.reset_index(drop=True),
-                  pd.read_csv(fn))
+timeseries = """
+Date,Open,High,Low,Close,Volume,Adj Close
+2015-08-28,198.50,199.839996,197.919998,199.240005,143298900,199.240005
+2015-08-27,197.020004,199.419998,195.210007,199.160004,266244700,199.160004
+2015-08-26,192.080002,194.789993,188.369995,194.679993,328058100,194.679993
+2015-08-25,195.429993,195.449997,186.919998,187.229996,353966700,187.229996
+2015-08-24,197.630005,197.630005,182.399994,189.550003,478672400,189.550003
+2015-08-21,201.729996,203.940002,197.520004,197.630005,328271500,197.630005
+2015-08-20,206.509995,208.289993,203.899994,204.009995,185865600,204.009995
+2015-08-19,209.089996,210.009995,207.350006,208.279999,167316300,208.279999
+2015-08-18,210.259995,210.679993,209.699997,209.929993,70043800,209.929993
+""".strip()
 
+csv_files = {'2014-01-01.csv': (b'name,amount,id\n'
+                                b'Alice,100,1\n'
+                                b'Bob,200,2\n'
+                                b'Charlie,300,3\n'),
+             '2014-01-02.csv': (b'name,amount,id\n'),
+             '2014-01-03.csv': (b'name,amount,id\n'
+                                b'Dennis,400,4\n'
+                                b'Edith,500,5\n'
+                                b'Frank,600,6\n')}
 
-def test_read_multiple_csv():
-    try:
-        with open('_foo.1.csv', 'w') as f:
-            f.write(text)
-        with open('_foo.2.csv', 'w') as f:
-            f.write(text)
-        df = dd.read_csv('_foo.*.csv', blocksize=30)
-        assert df.npartitions > 2
+tsv_files = {k: v.replace(b',', b'\t') for (k, v) in csv_files.items()}
 
-        assert (len(dd.read_csv('_foo.*.csv').compute()) ==
-                len(dd.read_csv('_foo.1.csv').compute()) * 2)
+expected = pd.concat([pd.read_csv(BytesIO(csv_files[k]))
+                      for k in sorted(csv_files)])
 
-    finally:
-        os.remove('_foo.1.csv')
-        os.remove('_foo.2.csv')
 
+csv_and_table = pytest.mark.parametrize('reader,files',
+                                        [(pd.read_csv, csv_files),
+                                         (pd.read_table, tsv_files)])
 
-def normalize_text(s):
-    return '\n'.join(map(str.strip, s.strip().split('\n')))
 
+@csv_and_table
+def test_pandas_read_text(reader, files):
+    b = files['2014-01-01.csv']
+    df = pandas_read_text(reader, b, b'', {})
+    assert list(df.columns) == ['name', 'amount', 'id']
+    assert len(df) == 3
+    assert df.id.sum() == 1 + 2 + 3
 
-def test_consistent_dtypes():
-    text = normalize_text("""
-    name,amount
-    Alice,100.5
-    Bob,-200.5
-    Charlie,300
-    Dennis,400
-    Edith,-500
-    Frank,600
-    """)
 
+@csv_and_table
+def test_pandas_read_text_kwargs(reader, files):
+    b = files['2014-01-01.csv']
+    df = pandas_read_text(reader, b, b'', {'usecols': ['name', 'id']})
+    assert list(df.columns) == ['name', 'id']
+
+
+@csv_and_table
+def test_pandas_read_text_dtype_coercion(reader, files):
+    b = files['2014-01-01.csv']
+    df = pandas_read_text(reader, b, b'', {}, {'amount': 'float'})
+    assert df.amount.dtype == 'float'
+
+
+@csv_and_table
+def test_pandas_read_text_with_header(reader, files):
+    b = files['2014-01-01.csv']
+    header, b = b.split(b'\n', 1)
+    header = header + b'\n'
+    df = pandas_read_text(reader, b, header, {})
+    assert list(df.columns) == ['name', 'amount', 'id']
+    assert len(df) == 3
+    assert df.id.sum() == 1 + 2 + 3
+
+
+@csv_and_table
+def test_text_blocks_to_pandas_simple(reader, files):
+    blocks = [[files[k]] for k in sorted(files)]
+    kwargs = {}
+    head = pandas_read_text(reader, files['2014-01-01.csv'], b'', {})
+    header = files['2014-01-01.csv'].split(b'\n')[0] + b'\n'
+
+    df = text_blocks_to_pandas(reader, blocks, header, head, kwargs,
+                               collection=True)
+    assert isinstance(df, dd.DataFrame)
+    assert list(df.columns) == ['name', 'amount', 'id']
+
+    values = text_blocks_to_pandas(reader, blocks, header, head, kwargs,
+                                   collection=False)
+    assert isinstance(values, list)
+    assert len(values) == 3
+    assert all(hasattr(item, 'dask') for item in values)
+
+    result = df.amount.sum().compute(get=get_sync)
+    assert result == (100 + 200 + 300 + 400 + 500 + 600)
+
+
+@csv_and_table
+def test_text_blocks_to_pandas_kwargs(reader, files):
+    blocks = [files[k] for k in sorted(files)]
+    blocks = [[b] for b in blocks]
+    kwargs = {'usecols': ['name', 'id']}
+    head = pandas_read_text(reader, files['2014-01-01.csv'], b'', kwargs)
+    header = files['2014-01-01.csv'].split(b'\n')[0] + b'\n'
+
+    df = text_blocks_to_pandas(reader, blocks, header, head, kwargs,
+                               collection=True)
+    assert list(df.columns) == ['name', 'id']
+    result = df.compute()
+    assert (result.columns == df.columns).all()
+
+
+@csv_and_table
+def test_text_blocks_to_pandas_blocked(reader, files):
+    header = files['2014-01-01.csv'].split(b'\n')[0] + b'\n'
+    blocks = []
+    for k in sorted(files):
+        b = files[k]
+        lines = b.split(b'\n')
+        blocks.append([b'\n'.join(bs) for bs in partition_all(2, lines)])
+
+    df = text_blocks_to_pandas(reader, blocks, header, expected.head(), {})
+    assert_eq(df.compute().reset_index(drop=True),
+              expected.reset_index(drop=True), check_dtype=False)
+
+    expected2 = expected[['name', 'id']]
+    df = text_blocks_to_pandas(reader, blocks, header, expected2.head(),
+                               {'usecols': ['name', 'id']})
+    assert_eq(df.compute().reset_index(drop=True),
+              expected2.reset_index(drop=True), check_dtype=False)
+
+
+csv_blocks = [[b'aa,bb\n1,1.0\n2,2.0', b'10,20\n30,40'],
+              [b'aa,bb\n1,1.0\n2,2.0', b'10,20\n30,40']]
+
+tsv_blocks = [[b'aa\tbb\n1\t1.0\n2\t2.0', b'10\t20\n30\t40'],
+              [b'aa\tbb\n1\t1.0\n2\t2.0', b'10\t20\n30\t40']]
+
+
+@pytest.mark.parametrize('reader,blocks', [(pd.read_csv, csv_blocks),
+                                           (pd.read_table, tsv_blocks)])
+def test_enforce_dtypes(reader, blocks):
+    head = reader(BytesIO(blocks[0][0]), header=0)
+    header = blocks[0][0].split(b'\n')[0] + b'\n'
+    dfs = text_blocks_to_pandas(reader, blocks, header, head, {},
+                                collection=False)
+    dfs = dask.compute(*dfs, get=get_sync)
+    assert all(df.dtypes.to_dict() == head.dtypes.to_dict() for df in dfs)
+
+
+@pytest.mark.parametrize('reader,blocks', [(pd.read_csv, csv_blocks),
+                                           (pd.read_table, tsv_blocks)])
+def test_enforce_columns(reader, blocks):
+    # Replace second header with different column name
+    blocks = [blocks[0], [blocks[1][0].replace(b'a', b'A'), blocks[1][1]]]
+    head = reader(BytesIO(blocks[0][0]), header=0)
+    header = blocks[0][0].split(b'\n')[0] + b'\n'
+    with pytest.raises(ValueError):
+        dfs = text_blocks_to_pandas(reader, blocks, header, head, {},
+                                    collection=False, enforce=True)
+        dask.compute(*dfs, get=get_sync)
+
+
+#############################
+#  read_csv and read_table  #
+#############################
+
+
+@pytest.mark.parametrize('dd_read,pd_read,text,sep',
+                         [(dd.read_csv, pd.read_csv, csv_text, ','),
+                          (dd.read_table, pd.read_table, tsv_text, '\t'),
+                          (dd.read_table, pd.read_table, tsv_text2, '\s+')])
+def test_read_csv(dd_read, pd_read, text, sep):
     with filetext(text) as fn:
-        df = dd.read_csv(fn, blocksize=30)
-        assert isinstance(df.amount.sum().compute(), float)
+        f = dd_read(fn, blocksize=30, lineterminator=os.linesep, sep=sep)
+        assert list(f.columns) == ['name', 'amount']
+        # index may be different
+        result = f.compute(get=dask.get).reset_index(drop=True)
+        assert_eq(result, pd_read(fn, sep=sep))
+
+
+@pytest.mark.parametrize('dd_read,pd_read,files',
+                         [(dd.read_csv, pd.read_csv, csv_files),
+                          (dd.read_table, pd.read_table, tsv_files)])
+def test_read_csv_files(dd_read, pd_read, files):
+    with filetexts(files, mode='b'):
+        df = dd_read('2014-01-*.csv')
+        assert_eq(df, expected, check_dtype=False)
+
+        fn = '2014-01-01.csv'
+        df = dd_read(fn)
+        expected2 = pd_read(BytesIO(files[fn]))
+        assert_eq(df, expected2, check_dtype=False)
+
+
+# After this point, we test just using read_csv, as all functionality
+# for both is implemented using the same code.
 
 
 def test_read_csv_index():
-    with filetext(text) as fn:
+    with filetext(csv_text) as fn:
         f = dd.read_csv(fn, blocksize=20).set_index('amount')
         result = f.compute(get=get_sync)
         assert result.index.name == 'amount'
@@ -111,18 +276,9 @@ def test_read_csv_index():
 
 
 def test_usecols():
-
-    datetime_csv_file = normalize_text("""
-    name,amount,when
-    Alice,100,2014-01-01
-    Bob,200,2014-01-01
-    Charlie,300,2014-01-01
-    Dan,400,2014-01-01
-    """)
-
-    with filetext(datetime_csv_file) as fn:
-        df = dd.read_csv(fn, blocksize=30, usecols=['when', 'amount'])
-        expected = pd.read_csv(fn, usecols=['when', 'amount'])
+    with filetext(timeseries) as fn:
+        df = dd.read_csv(fn, blocksize=30, usecols=['High', 'Low'])
+        expected = pd.read_csv(fn, usecols=['High', 'Low'])
         assert (df.compute().values == expected.values).all()
 
 
@@ -144,6 +300,22 @@ def test_skipinitialspace():
         assert df.amount.max().compute() == 600
 
 
+def test_consistent_dtypes():
+    text = normalize_text("""
+    name,amount
+    Alice,100.5
+    Bob,-200.5
+    Charlie,300
+    Dennis,400
+    Edith,-500
+    Frank,600
+    """)
+
+    with filetext(text) as fn:
+        df = dd.read_csv(fn, blocksize=30)
+        assert df.amount.compute().dtype == float
+
+
 def test_consistent_dtypes_2():
     text1 = normalize_text("""
     name,amount
@@ -158,36 +330,29 @@ def test_consistent_dtypes_2():
     2,-500
     Frank,600
     """)
-    try:
-        with open('_foo.1.csv', 'w') as f:
-            f.write(text1)
-        with open('_foo.2.csv', 'w') as f:
-            f.write(text2)
-        df = dd.read_csv('_foo.*.csv', blocksize=25)
 
-        assert df.amount.max().compute() == 600
-    finally:
-        pass
-        os.remove('_foo.1.csv')
-        os.remove('_foo.2.csv')
+    with filetexts({'foo.1.csv': text1, 'foo.2.csv': text2}):
+        df = dd.read_csv('foo.*.csv', blocksize=25)
+        assert df.name.dtype == object
+        assert df.name.compute().dtype == object
 
 
 @pytest.mark.slow
 def test_compression_multiple_files():
     with tmpdir() as tdir:
         f = gzip.open(os.path.join(tdir, 'a.csv.gz'), 'wb')
-        f.write(text.encode())
+        f.write(csv_text.encode())
         f.close()
 
         f = gzip.open(os.path.join(tdir, 'b.csv.gz'), 'wb')
-        f.write(text.encode())
+        f.write(csv_text.encode())
         f.close()
 
         with tm.assert_produces_warning(UserWarning):
             df = dd.read_csv(os.path.join(tdir, '*.csv.gz'),
                              compression='gzip')
 
-        assert len(df.compute()) == (len(text.split('\n')) - 1) * 2
+        assert len(df.compute()) == (len(csv_text.split('\n')) - 1) * 2
 
 
 def test_empty_csv_file():
@@ -197,146 +362,16 @@ def test_empty_csv_file():
         assert list(df.columns) == ['a', 'b']
 
 
-files = {'2014-01-01.csv': (b'name,amount,id\n'
-                            b'Alice,100,1\n'
-                            b'Bob,200,2\n'
-                            b'Charlie,300,3\n'),
-         '2014-01-02.csv': (b'name,amount,id\n'),
-         '2014-01-03.csv': (b'name,amount,id\n'
-                            b'Dennis,400,4\n'
-                            b'Edith,500,5\n'
-                            b'Frank,600,6\n')}
-
-
-header = files['2014-01-01.csv'].split(b'\n')[0] + b'\n'
-
-expected = pd.concat([pd.read_csv(BytesIO(files[k])) for k in sorted(files)])
-
-
-def test_bytes_read_csv():
-    b = files['2014-01-01.csv']
-    df = bytes_read_csv(b, b'', {})
-    assert list(df.columns) == ['name', 'amount', 'id']
-    assert len(df) == 3
-    assert df.id.sum() == 1 + 2 + 3
-
-
-def test_bytes_read_csv_kwargs():
-    b = files['2014-01-01.csv']
-    df = bytes_read_csv(b, b'', {'usecols': ['name', 'id']})
-    assert list(df.columns) == ['name', 'id']
-
-
-def test_bytes_read_csv_dtype_coercion():
-    b = files['2014-01-01.csv']
-    df = bytes_read_csv(b, b'', {}, {'amount': 'float'})
-    assert df.amount.dtype == 'float'
-
-
-def test_bytes_read_csv_with_header():
-    b = files['2014-01-01.csv']
-    header, b = b.split(b'\n', 1)
-    header = header + b'\n'
-    df = bytes_read_csv(b, header, {})
-    assert list(df.columns) == ['name', 'amount', 'id']
-    assert len(df) == 3
-    assert df.id.sum() == 1 + 2 + 3
-
-
-def test_read_csv_simple():
-    blocks = [[files[k]] for k in sorted(files)]
-    kwargs = {}
-    head = bytes_read_csv(files['2014-01-01.csv'], b'', {})
-
-    df = read_csv_from_bytes(blocks, header, head, kwargs, collection=True)
-    assert isinstance(df, dd.DataFrame)
-    assert list(df.columns) == ['name', 'amount', 'id']
-
-    values = read_csv_from_bytes(blocks, header, head, kwargs,
-                                 collection=False)
-    assert isinstance(values, list)
-    assert len(values) == 3
-    assert all(hasattr(item, 'dask') for item in values)
-
-    result = df.amount.sum().compute(get=get_sync)
-    assert result == (100 + 200 + 300 + 400 + 500 + 600)
-
-
-def test_kwargs():
-    blocks = [files[k] for k in sorted(files)]
-    blocks = [[b] for b in blocks]
-    kwargs = {'usecols': ['name', 'id']}
-    head = bytes_read_csv(files['2014-01-01.csv'], b'', kwargs)
-
-    df = read_csv_from_bytes(blocks, header, head, kwargs, collection=True)
-    assert list(df.columns) == ['name', 'id']
-    result = df.compute()
-    assert (result.columns == df.columns).all()
-
-
-def test_blocked():
-    blocks = []
-    for k in sorted(files):
-        b = files[k]
-        lines = b.split(b'\n')
-        blocks.append([b'\n'.join(bs) for bs in partition_all(2, lines)])
-
-    df = read_csv_from_bytes(blocks, header, expected.head(), {})
-    assert_eq(df.compute().reset_index(drop=True),
-              expected.reset_index(drop=True), check_dtype=False)
-
-    expected2 = expected[['name', 'id']]
-    df = read_csv_from_bytes(blocks, header, expected2.head(),
-                             {'usecols': ['name', 'id']})
-    assert_eq(df.compute().reset_index(drop=True),
-              expected2.reset_index(drop=True), check_dtype=False)
-
-
-def test_enforce_dtypes():
-    blocks = [[b'aa,bb\n1,1.0\n2.2.0', b'10,20\n30,40'],
-              [b'aa,bb\n1,1.0\n2.2.0', b'10,20\n30,40']]
-    head = pd.read_csv(BytesIO(blocks[0][0]), header=0)
-    dfs = read_csv_from_bytes(blocks, b'aa,bb\n', head, {},
-                              collection=False)
-    dfs = dask.compute(*dfs, get=get_sync)
-    assert all(df.dtypes.to_dict() == head.dtypes.to_dict() for df in dfs)
-
-
-def test_enforce_columns():
-    blocks = [[b'aa,bb\n1,1.0\n2.2.0', b'10,20\n30,40'],
-              [b'AA,bb\n1,1.0\n2.2.0', b'10,20\n30,40']]
-    head = pd.read_csv(BytesIO(blocks[0][0]), header=0)
-    with pytest.raises(ValueError):
-        dfs = read_csv_from_bytes(blocks, b'aa,bb\n', head, {},
-                                  collection=False, enforce=True)
-        dask.compute(*dfs, get=get_sync)
-
-
-def test_read_csv_files():
-    with filetexts(files, mode='b'):
-        df = dd.read_csv('2014-01-*.csv')
-        assert_eq(df, expected, check_dtype=False)
-
-        fn = '2014-01-01.csv'
-        df = dd.read_csv(fn)
-        expected2 = pd.read_csv(BytesIO(files[fn]))
-        assert_eq(df, expected2, check_dtype=False)
-
-
 def test_read_csv_sensitive_to_enforce():
-    with filetexts(files, mode='b'):
+    with filetexts(csv_files, mode='b'):
         a = dd.read_csv('2014-01-*.csv', enforce=True)
         b = dd.read_csv('2014-01-*.csv', enforce=False)
         assert a._name != b._name
 
 
-from dask.bytes.compression import compress, files as cfiles, seekable_files
-fmt_bs = [(fmt, None) for fmt in cfiles] + [(fmt, 10) for fmt in seekable_files]
-
-
 @pytest.mark.parametrize('fmt,blocksize', fmt_bs)
 def test_read_csv_compression(fmt, blocksize):
-    files2 = valmap(compress[fmt], files)
+    files2 = valmap(compress[fmt], csv_files)
     with filetexts(files2, mode='b'):
         df = dd.read_csv('2014-01-*.csv', compression=fmt, blocksize=blocksize)
         assert_eq(df.compute(get=get_sync).reset_index(drop=True),
@@ -344,7 +379,7 @@ def test_read_csv_compression(fmt, blocksize):
 
 
 def test_warn_non_seekable_files(capsys):
-    files2 = valmap(compress['gzip'], files)
+    files2 = valmap(compress['gzip'], csv_files)
     with filetexts(files2, mode='b'):
 
         # with tm.assert_produces_warning(UserWarning):
@@ -421,7 +456,7 @@ def test_auto_blocksize_csv(monkeypatch):
     monkeypatch.setattr(dask.dataframe.io.csv, 'read_bytes', mock_read_bytes)
 
     expected_block_size = auto_blocksize(total_memory, cpu_count)
-    with filetexts(files, mode='b'):
+    with filetexts(csv_files, mode='b'):
         dd.read_csv('2014-01-01.csv')
         assert mock_read_bytes.called
         assert mock_read_bytes.call_args[1]['blocksize'] == expected_block_size
@@ -446,106 +481,6 @@ def test_head_partial_line_fix():
         assert (df.dtypes == 'i8').all()
 
 
-def test_to_csv():
-    df = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
-                       'y': [1, 2, 3, 4]})
-
-    for npartitions in [1, 2]:
-        a = dd.from_pandas(df, npartitions)
-        with tmpdir() as dn:
-            a.to_csv(dn, index=False)
-            result = dd.read_csv(os.path.join(dn, '*')).compute().reset_index(drop=True)
-            assert_eq(result, df)
-
-        with tmpdir() as dn:
-            r = a.to_csv(dn, index=False, compute=False)
-            dask.compute(*r, get=get_sync)
-            result = dd.read_csv(os.path.join(dn, '*')).compute().reset_index(drop=True)
-            assert_eq(result, df)
-
-        with tmpdir() as dn:
-            fn = os.path.join(dn, 'data_*.csv')
-            a.to_csv(fn, index=False)
-            result = dd.read_csv(fn).compute().reset_index(drop=True)
-            assert_eq(result, df)
-
-
-def test_to_csv_multiple_files_cornercases():
-    df = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
-                       'y': [1, 2, 3, 4]})
-    a = dd.from_pandas(df, 2)
-    with tmpdir() as dn:
-        with pytest.raises(ValueError):
-            fn = os.path.join(dn, "data_*_*.csv")
-            a.to_csv(fn)
-
-    df16 = pd.DataFrame({'x': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-                               'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'],
-                         'y': [1, 2, 3, 4, 5, 6, 7, 8, 9,
-                               10, 11, 12, 13, 14, 15, 16]})
-    a = dd.from_pandas(df16, 16)
-    with tmpdir() as dn:
-        fn = os.path.join(dn, 'data_*.csv')
-        a.to_csv(fn, index=False)
-        result = dd.read_csv(fn).compute().reset_index(drop=True)
-        assert_eq(result, df16)
-
-    # test handling existing files when links are optimized out
-    a = dd.from_pandas(df, 2)
-    with tmpdir() as dn:
-        a.to_csv(dn, index=False)
-        fn = os.path.join(dn, 'data_*.csv')
-        a.to_csv(fn, mode='w', index=False)
-        result = dd.read_csv(fn).compute().reset_index(drop=True)
-        assert_eq(result, df)
-
-    # test handling existing files when links are optimized out
-    a = dd.from_pandas(df16, 16)
-    with tmpdir() as dn:
-        a.to_csv(dn, index=False)
-        fn = os.path.join(dn, 'data_*.csv')
-        a.to_csv(fn, mode='w', index=False)
-        result = dd.read_csv(fn).compute().reset_index(drop=True)
-        assert_eq(result, df16)
-
-
-@pytest.mark.xfail(reason="to_csv does not support compression")
-def test_to_csv_gzip():
-    df = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
-                       'y': [1, 2, 3, 4]}, index=[1., 2., 3., 4.])
-
-    for npartitions in [1, 2]:
-        a = dd.from_pandas(df, npartitions)
-        with tmpfile('csv') as fn:
-            a.to_csv(fn, compression='gzip')
-            result = pd.read_csv(fn, index_col=0, compression='gzip')
-            tm.assert_frame_equal(result, df)
-
-
-def test_to_csv_simple():
-    df0 = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
-                       'y': [1, 2, 3, 4]}, index=[1., 2., 3., 4.])
-    df = dd.from_pandas(df0, npartitions=2)
-    with tmpdir() as dir:
-        dir = str(dir)
-        df.to_csv(dir)
-        assert os.listdir(dir)
-        result = dd.read_csv(os.path.join(dir, '*')).compute()
-    assert (result.x.values == df0.x.values).all()
-
-
-def test_to_csv_series():
-    df0 = pd.Series(['a', 'b', 'c', 'd'], index=[1., 2., 3., 4.])
-    df = dd.from_pandas(df0, npartitions=2)
-    with tmpdir() as dir:
-        dir = str(dir)
-        df.to_csv(dir)
-        assert os.listdir(dir)
-        result = dd.read_csv(os.path.join(dir, '*'), header=None,
-                             names=['x']).compute()
-    assert (result.x == df0).all()
-
-
 def test_read_csv_raises_on_no_files():
     fn = '.not.a.real.file.csv'
     try:
@@ -556,7 +491,7 @@ def test_read_csv_raises_on_no_files():
 
 
 def test_read_csv_has_deterministic_name():
-    with filetext(text) as fn:
+    with filetext(csv_text) as fn:
         a = dd.read_csv(fn)
         b = dd.read_csv(fn)
         assert a._name == b._name
@@ -568,7 +503,7 @@ def test_read_csv_has_deterministic_name():
 
 
 def test_multiple_read_csv_has_deterministic_name():
-    with filetexts({'_foo.1.csv': text, '_foo.2.csv': text}):
+    with filetexts({'_foo.1.csv': csv_text, '_foo.2.csv': csv_text}):
         a = dd.read_csv('_foo.*.csv')
         b = dd.read_csv('_foo.*.csv')
 
@@ -583,7 +518,7 @@ def test_csv_with_integer_names():
 
 @pytest.mark.slow
 def test_read_csv_of_modified_file_has_different_name():
-    with filetext(text) as fn:
+    with filetext(csv_text) as fn:
         sleep(1)
         a = dd.read_csv(fn)
         sleep(1)
@@ -608,7 +543,7 @@ def test_report_dtype_correction_on_csvs():
 
 
 def test_index_col():
-    with filetext(text) as fn:
+    with filetext(csv_text) as fn:
         try:
             dd.read_csv(fn, blocksize=30, index_col='name')
             assert False
@@ -616,20 +551,6 @@ def test_index_col():
             assert 'set_index' in str(e)
 
 
-timeseries = """
-Date,Open,High,Low,Close,Volume,Adj Close
-2015-08-28,198.50,199.839996,197.919998,199.240005,143298900,199.240005
-2015-08-27,197.020004,199.419998,195.210007,199.160004,266244700,199.160004
-2015-08-26,192.080002,194.789993,188.369995,194.679993,328058100,194.679993
-2015-08-25,195.429993,195.449997,186.919998,187.229996,353966700,187.229996
-2015-08-24,197.630005,197.630005,182.399994,189.550003,478672400,189.550003
-2015-08-21,201.729996,203.940002,197.520004,197.630005,328271500,197.630005
-2015-08-20,206.509995,208.289993,203.899994,204.009995,185865600,204.009995
-2015-08-19,209.089996,210.009995,207.350006,208.279999,167316300,208.279999
-2015-08-18,210.259995,210.679993,209.699997,209.929993,70043800,209.929993
-""".strip()
-
-
 def test_read_csv_with_datetime_index_partitions_one():
     with filetext(timeseries) as fn:
         df = pd.read_csv(fn, index_col=0, header=0, usecols=[0, 4],
@@ -657,16 +578,6 @@ def test_read_csv_with_datetime_index_partitions_n():
         assert_eq(df, ddf)
 
 
-def test_from_pandas_with_datetime_index():
-    with filetext(timeseries) as fn:
-        df = pd.read_csv(fn, index_col=0, header=0, usecols=[0, 4],
-                         parse_dates=['Date'])
-        ddf = dd.from_pandas(df, 2)
-        assert_eq(df, ddf)
-        ddf = dd.from_pandas(df, chunksize=2)
-        assert_eq(df, ddf)
-
-
 @pytest.mark.parametrize('encoding', ['utf-16', 'utf-16-le', 'utf-16-be'])
 def test_encoding_gh601(encoding):
     ar = pd.Series(range(0, 100))
@@ -696,13 +607,12 @@ def test_read_csv_header_issue_823():
 
 
 def test_none_usecols():
-    with filetext(text) as fn:
+    with filetext(csv_text) as fn:
         df = dd.read_csv(fn, usecols=None)
         assert_eq(df, pd.read_csv(fn, usecols=None))
 
 
 def test_parse_dates_multi_column():
-
     pdmc_text = normalize_text("""
     ID,date,time
     10,2003-11-04,180036
@@ -729,7 +639,6 @@ def test_parse_dates_multi_column():
 
 
 def test_read_csv_sep():
-
     sep_text = normalize_text("""
     name###amount
     alice###100
@@ -744,16 +653,6 @@ def test_read_csv_sep():
         assert len(df) == len(ddf)
 
 
-def test_to_hdf_kwargs():
-    pytest.importorskip('tables')
-    df = pd.DataFrame({'A': ['a', 'aaaa']})
-    ddf = dd.from_pandas(df, npartitions=2)
-    with tmpfile('h5') as fn:
-        ddf.to_hdf(fn, 'foo4', format='table', min_itemsize=4)
-        df2 = pd.read_hdf(fn, 'foo4')
-        tm.assert_frame_equal(df, df2)
-
-
 def test_read_csv_slash_r():
     data = b'0,my\n1,data\n' * 1000 + b'2,foo\rbar'
     with filetext(data, mode='wb') as fn:
@@ -766,3 +665,107 @@ def test_read_csv_singleton_dtype():
     with filetext(data, mode='wb') as fn:
         assert_eq(pd.read_csv(fn, dtype=float),
                   dd.read_csv(fn, dtype=float))
+
+
+############
+#  to_csv  #
+############
+
+def test_to_csv():
+    df = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
+                       'y': [1, 2, 3, 4]})
+
+    for npartitions in [1, 2]:
+        a = dd.from_pandas(df, npartitions)
+        with tmpdir() as dn:
+            a.to_csv(dn, index=False)
+            result = dd.read_csv(os.path.join(dn, '*')).compute().reset_index(drop=True)
+            assert_eq(result, df)
+
+        with tmpdir() as dn:
+            r = a.to_csv(dn, index=False, compute=False)
+            dask.compute(*r, get=get_sync)
+            result = dd.read_csv(os.path.join(dn, '*')).compute().reset_index(drop=True)
+            assert_eq(result, df)
+
+        with tmpdir() as dn:
+            fn = os.path.join(dn, 'data_*.csv')
+            a.to_csv(fn, index=False)
+            result = dd.read_csv(fn).compute().reset_index(drop=True)
+            assert_eq(result, df)
+
+
+def test_to_csv_multiple_files_cornercases():
+    df = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
+                       'y': [1, 2, 3, 4]})
+    a = dd.from_pandas(df, 2)
+    with tmpdir() as dn:
+        with pytest.raises(ValueError):
+            fn = os.path.join(dn, "data_*_*.csv")
+            a.to_csv(fn)
+
+    df16 = pd.DataFrame({'x': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+                               'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'],
+                         'y': [1, 2, 3, 4, 5, 6, 7, 8, 9,
+                               10, 11, 12, 13, 14, 15, 16]})
+    a = dd.from_pandas(df16, 16)
+    with tmpdir() as dn:
+        fn = os.path.join(dn, 'data_*.csv')
+        a.to_csv(fn, index=False)
+        result = dd.read_csv(fn).compute().reset_index(drop=True)
+        assert_eq(result, df16)
+
+    # test handling existing files when links are optimized out
+    a = dd.from_pandas(df, 2)
+    with tmpdir() as dn:
+        a.to_csv(dn, index=False)
+        fn = os.path.join(dn, 'data_*.csv')
+        a.to_csv(fn, mode='w', index=False)
+        result = dd.read_csv(fn).compute().reset_index(drop=True)
+        assert_eq(result, df)
+
+    # test handling existing files when links are optimized out
+    a = dd.from_pandas(df16, 16)
+    with tmpdir() as dn:
+        a.to_csv(dn, index=False)
+        fn = os.path.join(dn, 'data_*.csv')
+        a.to_csv(fn, mode='w', index=False)
+        result = dd.read_csv(fn).compute().reset_index(drop=True)
+        assert_eq(result, df16)
+
+
+@pytest.mark.xfail(reason="to_csv does not support compression")
+def test_to_csv_gzip():
+    df = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
+                       'y': [1, 2, 3, 4]}, index=[1., 2., 3., 4.])
+
+    for npartitions in [1, 2]:
+        a = dd.from_pandas(df, npartitions)
+        with tmpfile('csv') as fn:
+            a.to_csv(fn, compression='gzip')
+            result = pd.read_csv(fn, index_col=0, compression='gzip')
+            tm.assert_frame_equal(result, df)
+
+
+def test_to_csv_simple():
+    df0 = pd.DataFrame({'x': ['a', 'b', 'c', 'd'],
+                       'y': [1, 2, 3, 4]}, index=[1., 2., 3., 4.])
+    df = dd.from_pandas(df0, npartitions=2)
+    with tmpdir() as dir:
+        dir = str(dir)
+        df.to_csv(dir)
+        assert os.listdir(dir)
+        result = dd.read_csv(os.path.join(dir, '*')).compute()
+    assert (result.x.values == df0.x.values).all()
+
+
+def test_to_csv_series():
+    df0 = pd.Series(['a', 'b', 'c', 'd'], index=[1., 2., 3., 4.])
+    df = dd.from_pandas(df0, npartitions=2)
+    with tmpdir() as dir:
+        dir = str(dir)
+        df.to_csv(dir)
+        assert os.listdir(dir)
+        result = dd.read_csv(os.path.join(dir, '*'), header=None,
+                             names=['x']).compute()
+    assert (result.x == df0).all()
diff --git a/dask/dataframe/io/tests/test_hdf.py b/dask/dataframe/io/tests/test_hdf.py
--- a/dask/dataframe/io/tests/test_hdf.py
+++ b/dask/dataframe/io/tests/test_hdf.py
@@ -395,6 +395,16 @@ def test_to_hdf_process():
         assert_eq(df, out)
 
 
+def test_to_hdf_kwargs():
+    pytest.importorskip('tables')
+    df = pd.DataFrame({'A': ['a', 'aaaa']})
+    ddf = dd.from_pandas(df, npartitions=2)
+    with tmpfile('h5') as fn:
+        ddf.to_hdf(fn, 'foo4', format='table', min_itemsize=4)
+        df2 = pd.read_hdf(fn, 'foo4')
+        tm.assert_frame_equal(df, df2)
+
+
 @pytest.mark.skipif(sys.version_info[:2] == (3, 3),
                     reason="Python3.3 uses pytest2.7.2, w/o warns method")
 def test_to_fmt_warns():
diff --git a/dask/dataframe/io/tests/test_io.py b/dask/dataframe/io/tests/test_io.py
--- a/dask/dataframe/io/tests/test_io.py
+++ b/dask/dataframe/io/tests/test_io.py
@@ -309,6 +309,18 @@ def test_from_pandas_single_row():
     assert_eq(ddf, df)
 
 
+def test_from_pandas_with_datetime_index():
+    df = pd.DataFrame({"Date": ["2015-08-28", "2015-08-27", "2015-08-26",
+                                "2015-08-25", "2015-08-24", "2015-08-21",
+                                "2015-08-20", "2015-08-19", "2015-08-18"],
+                       "Val": list(range(9))})
+    df.Date = df.Date.astype('datetime64')
+    ddf = dd.from_pandas(df, 2)
+    assert_eq(df, ddf)
+    ddf = dd.from_pandas(df, chunksize=2)
+    assert_eq(df, ddf)
+
+
 def test_DataFrame_from_dask_array():
     x = da.ones((10, 3), chunks=(4, 2))