Repository: dask/dask

Issue:
Add missing methods to Series
Pandas methods like `to_timestamp` are trivial to add to dask.dataframe.  We should go through the API and verify that we've implemented everything like this that is more-or-less trivial to do.



Discussion:

Resolving patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -2579,6 +2579,26 @@ def append(self, other, interleave_partitions=False):
             [self, other], join="outer", interleave_partitions=interleave_partitions
         )
 
+    @derived_from(pd.Series)
+    def dot(self, other, meta=no_default):
+        if not isinstance(other, _Frame):
+            raise TypeError("The second operand must be a dask array or dask dataframe")
+
+        if isinstance(other, DataFrame):
+            s = self.map_partitions(M.dot, other, token="dot", meta=meta)
+            return s.groupby(by=s.index).apply(
+                lambda x: x.sum(skipna=False), meta=s._meta_nonempty
+            )
+
+        def _dot_series(*args, **kwargs):
+            # .sum() is invoked on each partition before being applied to all
+            # partitions. The return type is expected to be a series, not a numpy object
+            return pd.Series(M.dot(*args, **kwargs))
+
+        return self.map_partitions(_dot_series, other, token="dot", meta=meta).sum(
+            skipna=False
+        )
+
     @derived_from(pd.DataFrame)
     def align(self, other, join="outer", axis=None, fill_value=None):
         meta1, meta2 = _emulate(


Test patch:
diff --git a/dask/dataframe/tests/test_dataframe.py b/dask/dataframe/tests/test_dataframe.py
--- a/dask/dataframe/tests/test_dataframe.py
+++ b/dask/dataframe/tests/test_dataframe.py
@@ -4545,3 +4545,47 @@ def test_assign_na_float_columns():
 
     assert df.compute()["a"].dtypes == "Float64"
     assert df.compute()["new_col"].dtypes == "Float64"
+
+
+def test_dot():
+    s1 = pd.Series([1, 2, 3, 4])
+    s2 = pd.Series([4, 5, 6, 6])
+    df = pd.DataFrame({"one": s1, "two": s2})
+
+    dask_s1 = dd.from_pandas(s1, npartitions=1)
+    dask_df = dd.from_pandas(df, npartitions=1)
+    dask_s2 = dd.from_pandas(s2, npartitions=1)
+
+    assert_eq(s1.dot(s2), dask_s1.dot(dask_s2))
+    assert_eq(s1.dot(df), dask_s1.dot(dask_df))
+
+    # With partitions
+    partitioned_s1 = dd.from_pandas(s1, npartitions=2)
+    partitioned_df = dd.from_pandas(df, npartitions=2)
+    partitioned_s2 = dd.from_pandas(s2, npartitions=2)
+
+    assert_eq(s1.dot(s2), partitioned_s1.dot(partitioned_s2))
+    assert_eq(s1.dot(df), partitioned_s1.dot(partitioned_df))
+
+    # Test passing meta kwarg
+    res = dask_s1.dot(dask_df, meta=pd.Series([1], name="test_series")).compute()
+    assert res.name == "test_series"
+
+    # Test validation of second operand
+    with pytest.raises(TypeError):
+        dask_s1.dot(da.array([1, 2, 3, 4]))
+
+
+def test_dot_nan():
+    # Test that nan inputs match pandas' behavior
+    s1 = pd.Series([1, 2, 3, 4])
+    dask_s1 = dd.from_pandas(s1, npartitions=1)
+
+    s2 = pd.Series([np.nan, np.nan, np.nan, np.nan])
+    dask_s2 = dd.from_pandas(s2, npartitions=1)
+
+    df = pd.DataFrame({"one": s1, "two": s2})
+    dask_df = dd.from_pandas(df, npartitions=1)
+
+    assert_eq(s1.dot(s2), dask_s1.dot(dask_s2))
+    assert_eq(s2.dot(df), dask_s2.dot(dask_df))