Repository: databricks/koalas

Issue:
Support ascending in sort_values
I want to sort values in descending order using sort_values, and although sort_values is supported, the ascending keyword is not.
<img width="1548" alt="Screen Shot 2019-04-22 at 8 06 20 AM" src="https://user-images.githubusercontent.com/6416014/56508431-18e74000-64d9-11e9-9e7a-85c764793996.png">

PandasLikeDataFrame: Add ascending to sort_values



Discussion:
See https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sort_values.html
@AbdealiJK can you update the patch? This would be useful to include in the next release.

Resolving patch:
diff --git a/databricks/koalas/frame.py b/databricks/koalas/frame.py
--- a/databricks/koalas/frame.py
+++ b/databricks/koalas/frame.py
@@ -661,8 +661,101 @@ def get(self, key, default=None):
         except (KeyError, ValueError, IndexError):
             return default
 
-    def sort_values(self, by):
-        return DataFrame(self._sdf.sort(by), self._metadata.copy())
+    def sort_values(self, by, ascending=True, inplace=False, na_position='last'):
+        """
+        Sort by the values along either axis.
+
+        Parameters
+        ----------
+        by : str or list of str
+        ascending : bool or list of bool, default True
+             Sort ascending vs. descending. Specify list for multiple sort
+             orders.  If this is a list of bools, must match the length of
+             the by.
+        inplace : bool, default False
+             if True, perform operation in-place
+        na_position : {'first', 'last'}, default 'last'
+             `first` puts NaNs at the beginning, `last` puts NaNs at the end
+
+        Returns
+        -------
+        sorted_obj : DataFrame
+
+        Examples
+        --------
+        >>> df = pd.DataFrame({
+        ...     'col1': ['A', 'A', 'B', np.nan, 'D', 'C'],
+        ...     'col2': [2, 1, 9, 8, 7, 4],
+        ...     'col3': [0, 1, 9, 4, 2, 3],
+        ... })
+        >>> df  # doctest: +NORMALIZE_WHITESPACE
+            col1 col2 col3
+        0     A    2    0
+        1     A    1    1
+        2     B    9    9
+        3   NaN    8    4
+        4     D    7    2
+        5     C    4    3
+
+        Sort by col1
+
+        >>> df.sort_values(by=['col1'])  # doctest: +NORMALIZE_WHITESPACE
+            col1 col2 col3
+        0     A    2    0
+        1     A    1    1
+        2     B    9    9
+        5     C    4    3
+        4     D    7    2
+        3   NaN    8    4
+
+        Sort by multiple columns
+
+        >>> df.sort_values(by=['col1', 'col2'])  # doctest: +NORMALIZE_WHITESPACE
+            col1 col2 col3
+        1     A    1    1
+        0     A    2    0
+        2     B    9    9
+        5     C    4    3
+        4     D    7    2
+        3   NaN    8    4
+
+        Sort Descending
+
+        >>> df.sort_values(by='col1', ascending=False)  # doctest: +NORMALIZE_WHITESPACE
+            col1 col2 col3
+        4     D    7    2
+        5     C    4    3
+        2     B    9    9
+        0     A    2    0
+        1     A    1    1
+        3   NaN    8    4
+        """
+        if isinstance(by, string_types):
+            by = [by]
+        if isinstance(ascending, bool):
+            ascending = [ascending] * len(by)
+        if len(ascending) != len(by):
+            raise ValueError('Length of ascending ({}) != length of by ({})'
+                             .format(len(ascending), len(by)))
+        if na_position not in ('first', 'last'):
+            raise ValueError("invalid na_position: '{}'".format(na_position))
+
+        # Mapper: Get a spark column function for (ascending, na_position) combination
+        # Note that 'asc_nulls_first' and friends were added as of Spark 2.4, see SPARK-23847.
+        mapper = {
+            (True, 'first'): lambda x: Column(getattr(x._jc, "asc_nulls_first")()),
+            (True, 'last'): lambda x: Column(getattr(x._jc, "asc_nulls_last")()),
+            (False, 'first'): lambda x: Column(getattr(x._jc, "desc_nulls_first")()),
+            (False, 'last'): lambda x: Column(getattr(x._jc, "desc_nulls_last")()),
+        }
+        by = [mapper[(asc, na_position)](self[colname]._scol)
+              for colname, asc in zip(by, ascending)]
+        kdf = DataFrame(self._sdf.sort(*by), self._metadata.copy())
+        if inplace:
+            self._sdf: spark.DataFrame = kdf._sdf
+            self._metadata = kdf._metadata
+        else:
+            return kdf
 
     def groupby(self, by):
         from databricks.koalas.groups import PandasLikeGroupBy


Test patch:
diff --git a/databricks/koalas/tests/test_dataframe.py b/databricks/koalas/tests/test_dataframe.py
--- a/databricks/koalas/tests/test_dataframe.py
+++ b/databricks/koalas/tests/test_dataframe.py
@@ -370,6 +370,27 @@ def test_to_datetime(self):
         self.assert_eq(pd.to_datetime(s, infer_datetime_format=True),
                        koalas.to_datetime(ds, infer_datetime_format=True))
 
+    def test_sort_values(self):
+        pdf = pd.DataFrame({'a': [1, 2, 3, 4, 5, None, 7],
+                           'b': [7, 6, 5, 4, 3, 2, 1]})
+        kdf = koalas.from_pandas(pdf)
+        self.assert_eq(repr(kdf.sort_values('b')), repr(pdf.sort_values('b')))
+        self.assert_eq(repr(kdf.sort_values(['b', 'a'])), repr(pdf.sort_values(['b', 'a'])))
+        self.assert_eq(
+            repr(kdf.sort_values(['b', 'a'], ascending=[False, True])),
+            repr(pdf.sort_values(['b', 'a'], ascending=[False, True])))
+
+        self.assertRaises(ValueError, lambda: kdf.sort_values(['b', 'a'], ascending=[False]))
+
+        self.assert_eq(
+            repr(kdf.sort_values(['b', 'a'], na_position='first')),
+            repr(pdf.sort_values(['b', 'a'], na_position='first')))
+
+        self.assertRaises(ValueError, lambda: kdf.sort_values(['b', 'a'], na_position='invalid'))
+
+        self.assert_eq(kdf.sort_values('b', inplace=True), pdf.sort_values('b', inplace=True))
+        self.assert_eq(repr(kdf), repr(pdf))
+
     def test_missing(self):
         kdf = self.kdf