codecov-io: ## [Current coverage](https://codecov.io/gh/napjon/krisk/pull/61?src=pr) is 87.72% (diff: 40.62%)
> Merging [#61](https://codecov.io/gh/napjon/krisk/pull/61?src=pr) into [master](https://codecov.io/gh/napjon/krisk/branch/master?src=pr) will decrease coverage by **4.16%**

```diff
@@             master        #61   diff @@
==========================================
  Files             8          8          
  Lines           370        391    +21   
  Methods           0          0          
  Messages          0          0          
  Branches         55         60     +5   
==========================================
+ Hits            340        343     +3   
- Misses           19         37    +18   
  Partials         11         11          
```

> Powered by [Codecov](https://codecov.io?src=pr). Last update [75dfe7d...550c497](https://codecov.io/gh/napjon/krisk/compare/75dfe7d414d23c488802ec242c913845253527bf...550c49756af45f8d46d670216dda7fd6ff8d3fb1?src=pr)
diff --git a/krisk/chart/core.py b/krisk/chart/core.py
index e62d98f..f4ae35a 100644
--- a/krisk/chart/core.py
+++ b/krisk/chart/core.py
@@ -419,6 +419,7 @@ class Chart(object):
     def _set_label_axes(self, xy, **kwargs):
         """Set label axes name and other customization"""
         assert xy in ['x','y']
+
         self.option[xy + 'Axis'].update(**kwargs)
         return self
 
@@ -468,6 +469,8 @@ class Chart(object):
         ------
         Chart object
         """
+        if self._kwargs_chart_.get('type', None) == 'bar_line':
+            raise NotImplementedError
         label_kwargs = dict(name=name,
                             nameLocation=axis_position,
                             nameGap=axis_gap,
diff --git a/krisk/plot/api.py b/krisk/plot/api.py
index dd4437d..d37dc89 100644
--- a/krisk/plot/api.py
+++ b/krisk/plot/api.py
@@ -21,32 +21,31 @@ def bar(df,
     x: string
         columns to be used as category axis
     y: string, default None
-        if None, use count of category value. otherwise aggregate based on y columns
-    category: string, default None
+        if None, use count of category. otherwise aggregate based on y columns
+    c: string, default None
         another grouping columns inside x-axis
     how: string, default None
-        to be passed to pd.group_by(x).aggregate(how). Can be mean,median, or any 
-        reduced operations.
+        to be passed to pd.group_by(x).aggregate(how). Can be mean,median,
+        or any reduced operations.
     stacked: Boolean, default False.
         Whether to stacked category on top of the other categories.
-    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
+    annotate: string, {'all', True, None} 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 False.
         If true, set to full area stacked chart. Only work if stacked is True.
     trendline: boolean, default False.
-        If true, add line that connected the bars. Only work if not category, category but stacked,
-        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 true, add line that connected the bars. Only work if not category,
+        category but stacked, or not full.
+    sort_on: {'index', 'values', int, function}, default 'index'.
         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 function, use it as aggregate (e.g. grouped.agg('mean' or np.mean))
         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.
+        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
     
@@ -54,8 +53,9 @@ def bar(df,
     -------
     Chart Object
     """
-    return make_chart(df,type='bar',x=x,y=y,c=c,how=how,stacked=stacked,full=full,
-                      trendline=trendline, sort_on=sort_on, sort_c_on=sort_c_on, ascending=ascending,
+    return make_chart(df,type='bar',x=x,y=y,c=c,how=how,stacked=stacked,
+                      full=full, trendline=trendline,
+                      sort_on=sort_on, sort_c_on=sort_c_on, ascending=ascending,
                       annotate='top' if annotate == True else annotate)
 
 
@@ -80,31 +80,32 @@ def line(df,
     x: string
         columns to be used as category axis
     y: string, default None
-        if None, use count of category value. otherwise aggregate based on y columns
+        if None, use count of category. otherwise aggregate based on y columns
     c: string, default None
         category column inside x-axis
     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 False.
+        to be passed to pd.group_by(x).aggregate(how). Can be mean,median,
+        or any reduced operations.
+    stacked: boolean, default False.
         Whether to stacked category on top of the other categories.
+    area: boolean, default False.
+        Whether to fill the area with line colors.
     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
+        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 False.
         If true, set to full area stacked chart. Only work if stacked is True.
     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.
+    sort_on: {'index', 'values', int, function}, default 'index'.
         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 function, use it as aggregate (e.g. grouped.agg('mean' or np.mean))
         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.
+        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
         
@@ -112,9 +113,57 @@ def line(df,
     -------
     Chart Object
     """
-    return make_chart(df,type='line',x=x,y=y,c=c,how=how,stacked=stacked,area=area,full=full,
-                      smooth=smooth, sort_on=sort_on, sort_c_on=sort_c_on, ascending=ascending,
-                      annotate='top' if annotate == True else annotate)
+    return make_chart(df,type='line',x=x,y=y,c=c,how=how,stacked=stacked,
+                      area=area,full=full, smooth=smooth,
+                      sort_on=sort_on, sort_c_on=sort_c_on, ascending=ascending,
+                      annotate='top' if annotate is True else annotate)
+
+
+def bar_line(df, x, ybar, yline, bar_aggfunc='mean', line_aggfunc='mean',
+             sort_on='index', ascending=True, is_distinct=False,
+             hide_split_line=True, style_tooltip=True):
+    """
+    Parameters
+    ----------
+    df: pd.DataFrame
+        data to be used for the chart
+    x: string
+        column to be used as category axis
+    ybar: string
+        column to be used as bar values
+    yline:
+        column to be used as line values
+    bar_aggfunc: string (mapping function) or function, default 'mean'
+        Function to use for aggregating groups on bar values
+    line_aggfunc: string (mapping function) or function, default 'mean'
+        Function to use for aggregating groups on line values
+    sort_on: {'index', 'ybar', 'yline'}, default 'index'
+        sorting x-axis. If index, sort on x. if either `ybar` or `yline`,
+        sort based on values
+    ascending: boolean, default True
+        sort ascending vs. descending
+    is_distinct: boolean, default False
+        Don't use aggregation on this data. Will use drop_duplicates instead.
+        Ignore `bar_aggfunc`, `line_aggfunc`, `sort_on`, `ascending` parameters.
+        sort_on deliberately disabled in is_distinct mode to allow already
+        sorted distinct data.
+    hide_split_line: boolean, default True
+        Whether to hide the split line of both y-axis.
+    style_tooltip: boolean, default True
+        Whether to offer help to style tooltip. If True, execute
+        `chart.set_tooltip_style(trigger='axis',axis_pointer='shadow')`
+
+    Returns
+    -------
+    Chart Object
+    """
+    return make_chart(df, x=x, ybar=ybar, yline=yline,
+                      bar_aggfunc=bar_aggfunc, line_aggfunc=line_aggfunc,
+                      is_distinct=is_distinct,
+                      sort_on=sort_on, ascending=ascending,
+                      hide_split_line=hide_split_line,
+                      style_tooltip=style_tooltip,
+                      c=None, type='bar_line')
 
 
 def hist(df,
@@ -140,9 +189,10 @@ def hist(df,
         Whether normalize the histogram
     stacked: Boolean, default False.
         Whether to stacked category on top of the other categories.
-    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
+    annotate: string, {'all',True, None} 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 False.
         Whether to add density to the plot
     
@@ -150,9 +200,9 @@ def hist(df,
     -------
     Chart Object
     """
-    return make_chart(df,type='hist',x=x,c=c,bins=bins,normed=normed,stacked=stacked,
-                      density=density,
-                      annotate='top' if annotate == True else annotate)
+    return make_chart(df,type='hist',x=x,c=c,bins=bins,normed=normed,
+                      stacked=stacked, density=density,
+                      annotate='top' if annotate is True else annotate)
    
 
 def scatter(df, x, y, s=None, c=None, saturate=None, size_px=(10, 70)):
@@ -167,7 +217,8 @@ def scatter(df, x, y, s=None, c=None, saturate=None, size_px=(10, 70)):
         Used as sizing value of the scatter points
     c: string, default None
         column used as grouping color category
-    saturation
+    saturate: string, default None
+        column to use for saturation
     size_px: tuple, default (10,70)
         boundary size, lower and upper limit in pixel for min-max scatter points
 
@@ -176,4 +227,5 @@ def scatter(df, x, y, s=None, c=None, saturate=None, size_px=(10, 70)):
     -------
     Chart Object
     """
-    return make_chart(df,type='scatter',x=x,y=y,s=s,c=c,saturate=saturate,size_px=size_px)
+    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/make_bar_line.py
similarity index 66%
rename from krisk/plot/bar_line.py
rename to krisk/plot/make_bar_line.py
index 931a83e..05f7201 100644
--- a/krisk/plot/bar_line.py
+++ b/krisk/plot/make_bar_line.py
@@ -12,7 +12,7 @@ def set_bar_line_chart(chart, df, x, c, **kwargs):
     chart_type = kwargs['type']
 
     if chart_type in ['bar', 'line']:
-        data = get_bar_line_data(df, x, c, **kwargs)
+        data = get_bar_or_line_data(df, x, c, **kwargs)
         chart.option['xAxis']['data'] = data.index.values.tolist()
 
     elif chart_type == 'hist':
@@ -20,6 +20,11 @@ def set_bar_line_chart(chart, df, x, c, **kwargs):
         data, bins = get_hist_data(df, x, c, **kwargs)
         chart.option['xAxis']['data'] = bins
 
+    elif chart_type == 'bar_line':
+        data = set_barline(df, x, chart, **kwargs)
+        chart.option['xAxis']['data'] = data.index.values.tolist()
+        return
+
     if c:
         # append data for every category
         for cat in data.columns:
@@ -50,20 +55,21 @@ def set_bar_line_chart(chart, df, x, c, **kwargs):
 
     if kwargs['annotate'] == 'top':
         series[-1]['label'] = d_annotate
-     # TODO: make annotate receive all kinds supported in echarts.
+    # TODO: make annotate receive all kinds supported in echarts.
 
     # Special Bar Condition: Trendline
     if kwargs['type'] == 'bar' and kwargs['trendline']:
-        trendline = {'name':'trendline', 'type': 'line',
+        trendline = {'name': 'trendline', 'type': 'line',
                      'lineStyle': {'normal': {'color': '#000'}}}
 
         if c and kwargs['stacked']:
-            trendline['data']  =  [0] * len(series[-1]['data'])
+            trendline['data'] = [0] * len(series[-1]['data'])
             trendline['stack'] = c
         elif c is None:
             trendline['data'] = series[0]['data']
         else:
-            raise AssertionError('Trendline must either stacked category, or not category')
+            raise AssertionError('Trendline must either stacked category,'
+                                 ' or not category')
 
         series.append(trendline)
 
@@ -81,8 +87,8 @@ def set_bar_line_chart(chart, df, x, c, **kwargs):
                    'lineStyle': {'normal': {'color': '#000'}}}
         chart.option['xAxis']['boundaryGap'] = False
 
-        # The density have to be closed at zero. So all of xAxis and series must be updated
-        # To incorporate the changes
+        # The density have to be closed at zero. So all of xAxis and series
+        # must be updated to incorporate the changes
         chart.option['xAxis']['data'] = [0] + chart.option['xAxis']['data'] + [0]
 
         for s in series:
@@ -93,12 +99,13 @@ def set_bar_line_chart(chart, df, x, c, **kwargs):
         elif c is None:
             density['data'] =  [0] + round_list(data) + [0] 
         else:
-            raise AssertionError('Density must either stacked category, or not category')
+            raise AssertionError('Density must either stacked category, '
+                                 'or not category')
 
         series.append(density)   
 
 
-def get_bar_line_data(df, x, c, y, **kwargs):
+def get_bar_or_line_data(df, x, c, y, **kwargs):
     """Get Bar and Line manipulated data"""
     
     if c and y:
@@ -115,7 +122,7 @@ def get_bar_line_data(df, x, c, y, **kwargs):
     else:
         data = df[x].value_counts()
 
-    #Specify sort_on and order method
+    # Specify sort_on and order method
     sort_on = kwargs['sort_on']
     descr_keys = pd.Series([0]).describe().keys().tolist()
     
@@ -126,11 +133,13 @@ def get_bar_line_data(df, x, c, y, **kwargs):
         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
+            val_deviation = sort_on(data) if callable(sort_on) 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'])
+            (data.sort_values(kwargs['sort_c_on'],
+                              inplace=True,
+                              ascending=kwargs['ascending']))
         else:
             data.sort_values(inplace=True, ascending=kwargs['ascending'])
 
@@ -157,3 +166,42 @@ def get_hist_data(df, x, c, **kwargs):
         data = pd.Series(y_val)
 
     return data, bins
+
+
+def set_barline(df, x, chart, **kwargs):
+    """Set Bar-Line charts"""
+
+    ybar = kwargs['ybar']
+    yline = kwargs['yline']
+
+    if kwargs['is_distinct'] is True:
+        data = df[[x, ybar, yline]].drop_duplicates(subset=[x]).copy()
+        data.index = data.pop(x)
+    else:
+        data = (df
+                .groupby(x)
+                .agg({ybar: kwargs['bar_aggfunc'],
+                      yline: kwargs['line_aggfunc']}))
+
+        assert kwargs['sort_on'] in ['index', 'ybar', 'yline']
+        if kwargs['sort_on'] == 'index':
+            data.sort_index(ascending=kwargs['ascending'], inplace=True)
+        else:
+            data.sort_values(kwargs[kwargs['sort_on']],
+                             ascending=kwargs['ascending'], inplace=True)
+
+    def get_series(col, type): return dict(name=col, type=type,
+                                           data=round_list(data[col]))
+    chart.option['series'] = [
+        get_series(ybar, 'bar'),
+        dict(yAxisIndex=1, **get_series(yline, 'line'))
+    ]
+
+    if kwargs['hide_split_line'] is True:
+        def get_yaxis(col): return {'name': col, 'splitLine': {'show': False}}
+        chart.option['yAxis'] = [get_yaxis(ybar), get_yaxis(yline)]
+
+    if kwargs['style_tooltip'] is True:
+        chart.set_tooltip_style(axis_pointer='shadow', trigger='axis')
+
+    return data
diff --git a/krisk/plot/make_chart.py b/krisk/plot/make_chart.py
index 68e3c41..c11b91d 100644
--- a/krisk/plot/make_chart.py
+++ b/krisk/plot/make_chart.py
@@ -33,20 +33,20 @@ def insert_series_data(data, x, chart_type, chart, cat=None):
 
 def make_chart(df, **kwargs):
 
-    from krisk.plot.bar_line import set_bar_line_chart
-    from krisk.plot.scatter_geo import set_scatter_chart
+    from krisk.plot.make_bar_line import set_bar_line_chart
+    from krisk.plot.make_scatter_geo import set_scatter_chart
 
     chart = Chart(**kwargs)
     chart._kwargs_chart_['data_columns'] = df.columns
-
     chart.set_xlabel(kwargs['x'])
+
     if kwargs.get('y', None):
         chart.set_ylabel(kwargs['y'])
 
     if kwargs['type'] == 'line':
         chart.set_tooltip_style(trigger='axis',axis_pointer='shadow')
 
-    if kwargs['type'] in ['bar', 'line', 'hist']:
+    if kwargs['type'] in ['bar', 'line', 'hist', 'bar_line']:
         set_bar_line_chart(chart, df, **kwargs)
 
     elif kwargs['type'] == 'scatter':

Add bar_line chart
Adding chart with bar and line with different series but same x-axis. For example,

``` Python
kk.bar_line(df, x, y_bar, y_line, c=None, .....)
```

diff --git a/krisk/plot/scatter_geo.py b/krisk/plot/make_scatter_geo.py
similarity index 100%
rename from krisk/plot/scatter_geo.py
rename to krisk/plot/make_scatter_geo.py
diff --git a/krisk/tests/test_plot.py b/krisk/tests/test_plot.py
index e3861b5..29ba507 100644
--- a/krisk/tests/test_plot.py
+++ b/krisk/tests/test_plot.py
@@ -1,6 +1,7 @@
 import json
 import pytest
 import krisk.plot as kk
+import numpy as np
 DATA_DIR = 'krisk/tests/data'
 read_option_tests = lambda f: json.load(open(DATA_DIR + '/' + f, 'r'))
 
@@ -11,6 +12,7 @@ def assert_barline_data(plot, true_option, test_legend=True):
     if test_legend:
         assert plot.option['legend']['data'] == true_option['legend']['data']
 
+
 def assert_scatter_data(plot, true_option):
     assert plot.option['series'][0]['data'] == true_option['series'][0]['data']
     assert plot.option['xAxis'] == true_option['xAxis']
@@ -49,12 +51,14 @@ def test_bar(gapminder):
 
     p4 = kk.bar(gapminder,'continent',y='gdpPercap',how='mean')
     opt4 = {'legend': {'data': []},
-                             'series': [{'data': [4426.026, 8955.554, 802.675, 3255.367, 19980.596],
+                             'series': [{'data': [4426.026, 8955.554, 802.675,
+                                                  3255.367, 19980.596],
                                'name': 'continent',
                                'type': 'bar'}],
                              'title': {'text': ''},
                              'tooltip': {'axisPointer': {'type': ''}},
-                             'xAxis': {'data': ['Africa', 'Americas', 'Asia', 'Europe', 'Oceania']},
+                             'xAxis': {'data': ['Africa', 'Americas', 'Asia',
+                                                'Europe', 'Oceania']},
                              'yAxis': {}}
     assert_barline_data(p4, opt4, test_legend=False) 
 
@@ -70,7 +74,8 @@ def test_trendline(gapminder):
     assert p1.option['series'][-1]['type'] == 'line'
     assert p1.option['series'][-1]['lineStyle'] == {'normal': {'color': '#000'}}
 
-    p2 = kk.bar(gapminder,'year',how='mean',y='pop',trendline=True,c='continent',stacked=True)
+    p2 = kk.bar(gapminder,'year',how='mean',y='pop',trendline=True,
+                c='continent',stacked=True)
     opt2 = read_option_tests('bar_year_pop_mean_continent_trendline.json')
     assert_barline_data(p2, opt2)
     assert p2.option['series'][-1]['data'] == opt2['series'][-1]['data']
@@ -97,12 +102,16 @@ def test_line(gapminder):
 
 def test_smooth_line(gapminder):
 
-    p = kk.line(gapminder[gapminder.year == 1952],'continent',y='pop',how='mean',smooth=True)
+    p = kk.line(gapminder[gapminder.year == 1952],'continent',y='pop',
+                how='mean',smooth=True)
     assert p.option['series'][0]['smooth'] == True
 
+
 def test_full_bar_line(gapminder):
-    bar = kk.bar(gapminder,'year',c='continent',y='pop',how='mean',stacked=True,full=True,annotate='all')
-    line = kk.line(gapminder,'year',c='continent',y='pop',how='mean',stacked=True,full=True,annotate='all')
+    bar = kk.bar(gapminder,'year',c='continent',y='pop',how='mean',
+                 stacked=True,full=True,annotate='all')
+    line = kk.line(gapminder,'year',c='continent',y='pop',how='mean',
+           stacked=True,full=True,annotate='all')
    
     for i in range(len(bar.option['series'])):
         bar.option['series'][i].pop('type')
@@ -116,11 +125,15 @@ def test_full_bar_line(gapminder):
     assert_barline_data(bar, true_option)
     assert_barline_data(line, 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')
+    p = kk.line(gapminder,'year', y='pop', how='mean',c='continent',
+                sort_on= np.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['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,
@@ -157,15 +170,18 @@ def test_density(gapminder):
 
     p1 = kk.hist(gapminder,'lifeExp',density=True)
     assert p1.option['series'][0]['data'] == [0, 4, 2, 7, 2, 2, 3, 5, 13, 16, 6]
-    assert p1.option['series'][-1] == {'data': [0, 4, 2, 7, 2, 2, 3, 5, 13, 16, 6, 0],
+    assert p1.option['series'][-1] == {'data': [0, 4, 2, 7, 2, 2,
+                                                3, 5, 13, 16, 6, 0],
                                    'lineStyle': {'normal': {'color': '#000'}},
                                    'name': 'density',
                                    'smooth': True,
                                    'type': 'line'}
     assert p1.option['xAxis']['boundaryGap'] ==  False
-    assert p1.option['xAxis']['data'] ==  [0, 28, 34, 39, 44, 49, 55, 60, 65, 70, 75, 81, 0]
+    assert p1.option['xAxis']['data'] ==  [0, 28, 34, 39, 44, 49, 55,
+                                           60, 65, 70, 75, 81, 0]
 
-    p2 = kk.hist(gapminder,'lifeExp',bins=10,c='continent',stacked=True,density=True)
+    p2 = kk.hist(gapminder,'lifeExp',bins=10,c='continent',
+                 stacked=True,density=True)
     opt2 = read_option_tests('hist_lifeExp_b10_continent_density.json')
     assert_barline_data(p2, opt2)
 
@@ -175,7 +191,6 @@ def test_density(gapminder):
         pass
 
 
-
 def test_scatter(gapminder):
     # Simple Scatter
     p1 = kk.scatter(gapminder[gapminder.year == 1952],'pop','lifeExp')
@@ -204,6 +219,33 @@ def test_scatter(gapminder):
 
     # Scatter
     
-    p3 = kk.scatter(gapminder[gapminder.year == 1952], 'lifeExp', 'gdpPercap', s='pop')
+    p3 = kk.scatter(gapminder[gapminder.year == 1952],
+                    'lifeExp', 'gdpPercap', s='pop')
     opt3 = read_option_tests('scatter_single.json')
     assert_scatter_data(p3, opt3)
+
+
+def test_bar_line(gapminder):
+
+    p1 = kk.bar_line(gapminder, 'continent', 'lifeExp', 'gdpPercap')
+    assert p1.option['series'][0] == {'data': [59.03, 69.06, 37.479,
+                                               68.433, 74.663],
+                                      'name': 'lifeExp',
+                                      'type': 'bar'}
+    assert p1.option['series'][-1] == {'data': [4426.026, 8955.554, 802.675,
+                                                3255.367, 19980.596],
+                                       'name': 'gdpPercap',
+                                       'type': 'line',
+                                       'yAxisIndex': 1}
+    assert p1.option['xAxis']['data'] == ['Africa', 'Americas', 'Asia',
+                                          'Europe', 'Oceania']
+
+    p2 = kk.bar_line(gapminder, 'continent', 'lifeExp', 'gdpPercap',
+                     is_distinct=True)
+    assert p2.option['series'][0]['data'] == [43.077, 62.485, 28.801,
+                                              55.23, 69.12]
+    assert p2.option['series'][-1]['data'] == [2449.008, 5911.315, 779.445,
+                                               1601.056, 10039.596]
+    assert p2.option['xAxis']['data'] == ['Africa', 'Americas', 'Asia',
+                                          'Europe', 'Oceania']
+

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@cbf39a0b5f2876a7cf1ce45361be3ba79aba4dc7#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_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_sort_bar_line krisk/tests/test_plot.py::test_hist krisk/tests/test_plot.py::test_density krisk/tests/test_plot.py::test_scatter krisk/tests/test_plot.py::test_bar_line
krisk/plot/make_chart.py krisk/plot/bar_line.py krisk/chart/core.py krisk/plot/api.py
krisk/plot/make_bar_line.py krisk/plot/make_chart.py krisk/chart/core.py krisk/plot/api.py