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)
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
 
 

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
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):
 

pip install -e .
parse_log_pytest
requirements.txt
pytest coverage
apt-get update apt-get install -y gcc
3.5
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
anyio==3.6.2
argon2-cffi==21.3.0
argon2-cffi-bindings==21.2.0
async-generator==1.10
attrs==22.2.0
Babel==2.11.0
backcall==0.2.0
bleach==4.1.0
certifi==2021.5.30
cffi==1.15.1
charset-normalizer==2.0.12
comm==0.1.4
contextvars==2.4
coverage==6.2
dataclasses==0.8
decorator==5.1.1
defusedxml==0.7.1
entrypoints==0.4
idna==3.10
immutables==0.19
importlib-metadata==4.8.3
iniconfig==1.1.1
ipykernel==5.5.6
ipython==7.16.3
ipython-genutils==0.2.0
ipywidgets==7.8.5
jedi==0.17.2
Jinja2==3.0.3
json5==0.9.16
jsonschema==3.2.0
jupyter==1.1.1
jupyter-client==7.1.2
jupyter-console==6.4.3
jupyter-core==4.9.2
jupyter-server==1.13.1
jupyterlab==3.2.9
jupyterlab-pygments==0.1.2
jupyterlab-server==2.10.3
jupyterlab_widgets==1.1.11
-e git+https://github.com/napjon/krisk.git@19fb69026ff1339649fac5ad82548ccbdc7b8d19#egg=krisk
MarkupSafe==2.0.1
mistune==0.8.4
nbclassic==0.3.5
nbclient==0.5.9
nbconvert==6.0.7
nbformat==5.1.3
nest-asyncio==1.6.0
notebook==6.4.10
numpy==1.19.5
packaging==21.3
pandas==1.1.5
pandocfilters==1.5.1
parso==0.7.1
pexpect==4.9.0
pickleshare==0.7.5
pluggy==1.0.0
prometheus-client==0.17.1
prompt-toolkit==3.0.36
ptyprocess==0.7.0
py==1.11.0
pycparser==2.21
Pygments==2.14.0
pyparsing==3.1.4
pyrsistent==0.18.0
pytest==7.0.1
python-dateutil==2.9.0.post0
pytz==2025.2
pyzmq==25.1.2
requests==2.27.1
Send2Trash==1.8.3
six==1.17.0
sniffio==1.2.0
terminado==0.12.1
testpath==0.6.0
tomli==1.2.3
tornado==6.1
traitlets==4.3.3
typing_extensions==4.1.1
urllib3==1.26.20
wcwidth==0.2.13
webencodings==0.5.1
websocket-client==1.3.1
widgetsnbextension==3.6.10
zipp==3.6.0

name: krisk
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - anyio==3.6.2
      - argon2-cffi==21.3.0
      - argon2-cffi-bindings==21.2.0
      - async-generator==1.10
      - attrs==22.2.0
      - babel==2.11.0
      - backcall==0.2.0
      - bleach==4.1.0
      - cffi==1.15.1
      - charset-normalizer==2.0.12
      - comm==0.1.4
      - contextvars==2.4
      - coverage==6.2
      - dataclasses==0.8
      - decorator==5.1.1
      - defusedxml==0.7.1
      - entrypoints==0.4
      - idna==3.10
      - immutables==0.19
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - ipykernel==5.5.6
      - ipython==7.16.3
      - ipython-genutils==0.2.0
      - ipywidgets==7.8.5
      - jedi==0.17.2
      - jinja2==3.0.3
      - json5==0.9.16
      - jsonschema==3.2.0
      - jupyter==1.1.1
      - jupyter-client==7.1.2
      - jupyter-console==6.4.3
      - jupyter-core==4.9.2
      - jupyter-server==1.13.1
      - jupyterlab==3.2.9
      - jupyterlab-pygments==0.1.2
      - jupyterlab-server==2.10.3
      - jupyterlab-widgets==1.1.11
      - markupsafe==2.0.1
      - mistune==0.8.4
      - nbclassic==0.3.5
      - nbclient==0.5.9
      - nbconvert==6.0.7
      - nbformat==5.1.3
      - nest-asyncio==1.6.0
      - notebook==6.4.10
      - numpy==1.19.5
      - packaging==21.3
      - pandas==1.1.5
      - pandocfilters==1.5.1
      - parso==0.7.1
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pluggy==1.0.0
      - prometheus-client==0.17.1
      - prompt-toolkit==3.0.36
      - ptyprocess==0.7.0
      - py==1.11.0
      - pycparser==2.21
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pyrsistent==0.18.0
      - pytest==7.0.1
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyzmq==25.1.2
      - requests==2.27.1
      - send2trash==1.8.3
      - six==1.17.0
      - sniffio==1.2.0
      - terminado==0.12.1
      - testpath==0.6.0
      - tomli==1.2.3
      - tornado==6.1
      - traitlets==4.3.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - wcwidth==0.2.13
      - webencodings==0.5.1
      - websocket-client==1.3.1
      - widgetsnbextension==3.6.10
      - zipp==3.6.0
prefix: /opt/conda/envs/krisk

krisk/tests/test_plot.py::test_sort_bar_line
krisk/tests/test_plot.py::test_bar krisk/tests/test_plot.py::test_trendline krisk/tests/test_plot.py::test_line krisk/tests/test_plot.py::test_smooth_line krisk/tests/test_plot.py::test_full_bar_line krisk/tests/test_plot.py::test_hist krisk/tests/test_plot.py::test_density krisk/tests/test_plot.py::test_scatter
krisk/plot/bar_line.py krisk/plot/api.py
krisk/plot/bar_line.py krisk/plot/api.py