Repository: napjon/krisk

Issue:
Add Bar Mode for Sequential, Diverging, and Qualitative
add 'mode' as keyword parameter for bar chart.

* If None, treat the bar mode as "Qualitative", and sort on lexicographical order.
* If Boolean, treat the bar as "Sequential", whether the order sort ascending.
* If string, either median or mean and deviation from that value
* if integer, treat as value and deviate from that value

Discussion:
codecov-io: ## [Current coverage](https://codecov.io/gh/napjon/krisk/pull/53?src=pr) is 92.03% (diff: 71.42%)
> Merging [#53](https://codecov.io/gh/napjon/krisk/pull/53?src=pr) into [0.2-develop](https://codecov.io/gh/napjon/krisk/branch/0.2-develop?src=pr) will decrease coverage by **0.84%**

```diff
@@           0.2-develop        #53   diff @@
=============================================
  Files                8          8          
  Lines              351        364    +13   
  Methods              0          0          
  Messages             0          0          
  Branches            50         54     +4   
=============================================
+ Hits               326        335     +9   
- Misses              17         18     +1   
- Partials             8         11     +3   
```

> Powered by [Codecov](https://codecov.io?src=pr). Last update [19fb690...a83683d](https://codecov.io/gh/napjon/krisk/compare/19fb69026ff1339649fac5ad82548ccbdc7b8d19...a83683dc71a8efa67aeb5a3367d22d2d43823b4e?src=pr)

Patch:
diff --git a/krisk/plot/api.py b/krisk/plot/api.py
index 281688f..dd4437d 100644
--- a/krisk/plot/api.py
+++ b/krisk/plot/api.py
@@ -9,7 +9,10 @@ def bar(df,
         stacked=False,
         annotate=None,
         full=False,
-        trendline=False):
+        trendline=False,
+        sort_on='index',
+        sort_c_on=None,
+        ascending=True):
     """
     Parameters
     ----------
@@ -17,33 +20,42 @@ def bar(df,
         data to be used for the chart
     x: string
         columns to be used as category axis
-    y: string, default to None
+    y: string, default None
         if None, use count of category value. otherwise aggregate based on y columns
-    category: string, default to None
+    category: string, default None
         another grouping columns inside x-axis
-    how: string, default to None
+    how: string, default None
         to be passed to pd.group_by(x).aggregate(how). Can be mean,median, or any 
         reduced operations.
-    stacked: Boolean, default to False.
+    stacked: Boolean, default False.
         Whether to stacked category on top of the other categories.
-    annotate: string, {'all',True} default to None
+    annotate: string, {'all',True} default None
         if True, annotate value on top of the plot element. If stacked is also True, annotate the 
         last category. if 'all' and stacked, annotate all category
-    full: boolean, default to False.
+    full: boolean, default False.
         If true, set to full area stacked chart. Only work if stacked is True.
-    trendline: boolean, default to False.
+    trendline: boolean, default False.
         If true, add line that connected the bars. Only work if not category, category but stacked,
-        or not full.  
+        or not full.
+    sort_on: {'index', 'values', int, 'count', 'mean', 'std', 'min', '25%', '50%', '75%', 'max'},
+         default 'index'.
+        Add sort mode. Only work when c is None.
+        If index, sort index on lexicographical order. use as s.sort_index()
+        if values, sort based on values. Use as s.sort_values()
+        If string, deviation from value provided by pd.Series.describe()
+        if integer, treat as value and deviate from that value
+    sort_c_on: string, default None.
+        specify a category as basis sort value if c is specified. Must be specified when use 
+        sort_on other than default value.
+    ascending: boolean, default True
+        sort ascending vs. descending
     
     Returns
     -------
     Chart Object
     """
-
-    # TODO: add optional argument trendline
-
     return make_chart(df,type='bar',x=x,y=y,c=c,how=how,stacked=stacked,full=full,
-                      trendline=trendline,
+                      trendline=trendline, sort_on=sort_on, sort_c_on=sort_c_on, ascending=ascending,
                       annotate='top' if annotate == True else annotate)
 
 
@@ -56,7 +68,10 @@ def line(df,
          area=False,
          annotate=None,
          full=False,
-         smooth=False):
+         smooth=False,
+         sort_on='index',
+         sort_c_on=None,
+         ascending=True):
     """
     Parameters
     ----------
@@ -64,29 +79,41 @@ def line(df,
         data to be used for the chart
     x: string
         columns to be used as category axis
-    y: string, default to None
+    y: string, default None
         if None, use count of category value. otherwise aggregate based on y columns
-    c: string, default to None
+    c: string, default None
         category column inside x-axis
-    how: string, default to None
+    how: string, default None
         to be passed to pd.group_by(x).aggregate(how). Can be mean,median, or any 
         reduced operations.
-    stacked: Boolean, default to False.
+    stacked: Boolean, default False.
         Whether to stacked category on top of the other categories.
-    annotate: string, {'all',True} default to None
+    annotate: string, {'all',True} default None
         if True, annotate value on top of the plot element. If stacked is also True, annotate the last
         category. if 'all' and stacked, annotate all category
-    full: boolean, default to False.
+    full: boolean, default False.
         If true, set to full area stacked chart. Only work if stacked is True.
-    smooth: boolean, default to False.
+    smooth: boolean, default False.
         If true, smooth the line.
-    
+   sort_on: {'index', 'values', int, 'count', 'mean', 'std', 'min', '25%', '50%', '75%', 'max'},
+         default 'index'.
+        Add sort mode. Only work when c is None.
+        If index, sort index on lexicographical order. use as s.sort_index()
+        if values, sort based on values. Use as s.sort_values()
+        If string, deviation from value provided by pd.Series.describe()
+        if integer, treat as value and deviate from that value
+    sort_c_on: string, default None.
+        specify a category as basis sort value if c is specified. Must be specified when use 
+        sort_on other than default value.
+    ascending: boolean, default True
+        sort ascending vs. descending
+        
     Returns
     -------
     Chart Object
     """
     return make_chart(df,type='line',x=x,y=y,c=c,how=how,stacked=stacked,area=area,full=full,
-                      smooth=smooth,
+                      smooth=smooth, sort_on=sort_on, sort_c_on=sort_c_on, ascending=ascending,
                       annotate='top' if annotate == True else annotate)
 
 
@@ -105,18 +132,18 @@ def hist(df,
         data to be used for the chart
     x: string
         columns to be used as category axis
-    c: string, default to None
+    c: string, default None
         another grouping columns inside x-axis
-    bins: int, default to 10
+    bins: int, default 10
         Set number of bins in histogram
-    normed: boolean, default to False
+    normed: boolean, default False
         Whether normalize the histogram
-    stacked: Boolean, default to False.
+    stacked: Boolean, default False.
         Whether to stacked category on top of the other categories.
-    annotate: string, {'all',True} default to None
+    annotate: string, {'all',True} default None
         if True, annotate value on top of the plot element. If stacked is also True, annotate the last
         category. if 'all' and stacked, annotate all category
-    density: boolean, default to False.
+    density: boolean, default False.
         Whether to add density to the plot
     
     Returns
@@ -136,12 +163,12 @@ def scatter(df, x, y, s=None, c=None, saturate=None, size_px=(10, 70)):
         data to be used for the chart
     x,y: string, columns in pd.DataFrame
         Used as coordinate in scatter chart
-    s: string, columns in pd.DataFrame default to None
+    s: string, columns in pd.DataFrame default None
         Used as sizing value of the scatter points
-    c: string, default to None
+    c: string, default None
         column used as grouping color category
     saturation
-    size_px: tuple, default to (10,70)
+    size_px: tuple, default (10,70)
         boundary size, lower and upper limit in pixel for min-max scatter points
 
         
@@ -149,5 +176,4 @@ def scatter(df, x, y, s=None, c=None, saturate=None, size_px=(10, 70)):
     -------
     Chart Object
     """
-    #TODO add saturation
     return make_chart(df,type='scatter',x=x,y=y,s=s,c=c,saturate=saturate,size_px=size_px)
diff --git a/krisk/plot/bar_line.py b/krisk/plot/bar_line.py
index 73ffa54..931a83e 100644
--- a/krisk/plot/bar_line.py
+++ b/krisk/plot/bar_line.py
@@ -95,11 +95,7 @@ def set_bar_line_chart(chart, df, x, c, **kwargs):
         else:
             raise AssertionError('Density must either stacked category, or not category')
 
-        series.append(density)
-
-
-    
-   
+        series.append(density)   
 
 
 def get_bar_line_data(df, x, c, y, **kwargs):
@@ -119,11 +115,29 @@ def get_bar_line_data(df, x, c, y, **kwargs):
     else:
         data = df[x].value_counts()
 
+    #Specify sort_on and order method
+    sort_on = kwargs['sort_on']
+    descr_keys = pd.Series([0]).describe().keys().tolist()
+    
+    if isinstance(sort_on, str):
+        assert sort_on in ['index','values'] + descr_keys
+
+    if sort_on == 'index':
+        data.sort_index(inplace=True, ascending=kwargs['ascending'])
+    else:
+        if sort_on != 'values':
+            val_deviation = data.describe().loc[sort_on] if isinstance(sort_on, str) else sort_on
+            data = data - val_deviation
+        if c:
+            assert kwargs['sort_c_on'] is not None
+            data.sort_values(kwargs['sort_c_on'], inplace=True, ascending=kwargs['ascending'])
+        else:
+            data.sort_values(inplace=True, ascending=kwargs['ascending'])
 
+    # Stacked when category
     if c and kwargs['stacked'] and kwargs['full']:
         data = data.div(data.sum(1),axis=0)
 
-            
     return data
 
 


Test patch:
diff --git a/krisk/tests/test_plot.py b/krisk/tests/test_plot.py
index ebbd7f5..26fc661 100644
--- a/krisk/tests/test_plot.py
+++ b/krisk/tests/test_plot.py
@@ -103,6 +103,25 @@ def test_full_bar_line(gapminder):
 
     assert remove_name_label(bar).option == remove_name_label(line).option == true_option
 
+def test_sort_bar_line(gapminder):
+    p = kk.line(gapminder,'year', y='pop', how='mean',c='continent', sort_on='mean', sort_c_on='Americas')
+
+    assert p.option['xAxis']['data'] == [1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, 2002, 2007]
+    assert p.option['legend']['data'] == ['Africa', 'Americas', 'Asia', 'Europe', 'Oceania']
+    assert p.option['series'][0] == {'data': [-10595881.167,
+                                              -9604550.167,
+                                              -8874458.167,
+                                              -7114907.167,
+                                              -5114619.167,
+                                              -2722602.167,
+                                              158346.833,
+                                              3379549.833,
+                                              6422966.833,
+                                              9196608.833,
+                                              11411735.833,
+                                              13457809.833],
+                                     'name': 'Africa',
+                                     'type': 'line'}
 
 def test_hist(gapminder):