diff --git a/src/iplotter/__init__.py b/src/iplotter/__init__.py
new file mode 100644
index 0000000..4cd17ca
--- /dev/null
+++ b/src/iplotter/__init__.py
@@ -0,0 +1,8 @@
+from .base_plotter import IPlotter
+# from .export import VirtualBrowser
+from .c3_plotter import C3Plotter
+from .plotly_plotter import PlotlyPlotter
+from .chartjs_plotter import ChartJSPlotter
+from .chartist_plotter import ChartistPlotter
+from .google_plotter import GCPlotter
+__version__ = '0.4.3'
diff --git a/src/iplotter/base_plotter.py b/src/iplotter/base_plotter.py
new file mode 100644
index 0000000..70c86ee
--- /dev/null
+++ b/src/iplotter/base_plotter.py
@@ -0,0 +1,55 @@
+from abc import ABCMeta, abstractmethod
+import re
+import time
+# from selenium import webdriver
+import os
+
+
+class IPlotter(object):
+ """Abstract IPlotter"""
+
+ __metaclass__ = ABCMeta
+ iframe = ''
+ invalid_name_pattern = re.compile(r'[^a-zA-Z0-9_\-\. ]+')
+
+ def __init__(self):
+ super(IPlotter, self).__init__()
+
+ @classmethod
+ def is_valid_name(cls, name):
+ '''
+ check whether plot div id or filenname are valid
+ '''
+ if (cls.invalid_name_pattern.search(name)):
+ return False
+ else:
+ return True
+
+ @abstractmethod
+ def render(self):
+ '''
+ render the data in HTML template
+ '''
+ pass
+
+ @abstractmethod
+ def plot(self):
+ '''
+ output an iframe containing the plot in the notebook without saving
+ '''
+ pass
+
+ @abstractmethod
+ def save(self):
+ '''
+ save the rendered html to a file in the same directory as the notebook
+ '''
+ pass
+
+ @abstractmethod
+ def plot_and_save(self):
+ '''
+ save the rendered html to a file and return an IFrame to display the
+ plot in the notebook
+ '''
+ pass
diff --git a/src/iplotter/c3_plotter.py b/src/iplotter/c3_plotter.py
new file mode 100644
index 0000000..cbfd12f
--- /dev/null
+++ b/src/iplotter/c3_plotter.py
@@ -0,0 +1,386 @@
+from jinja2 import Template
+from IPython.display import IFrame, HTML
+import os
+import json
+from .base_plotter import IPlotter
+import math
+
+
+class C3Plotter(IPlotter):
+ """
+ Class for creating c3.js charts in ipython notebook
+ """
+
+ head = '''
+
+
+
+
+
+
+
+ '''
+
+ template = '''
+
{{title}}
+
+
+ '''
+
+ def __init__(self):
+ super(C3Plotter, self).__init__()
+
+ def render(self,
+ data,
+ div_id="chart",
+ custom_css='',
+ title="",
+ head="",
+ y_axis_tick_format='',
+ secondary_y_axis_tick_format=''
+ ,
+ **kwargs):
+ '''
+ render the data in HTML template
+ '''
+ try:
+ data = self.pandas_data(data, **kwargs)
+ except AttributeError:
+ pass
+
+ if not self.is_valid_name(div_id):
+ raise ValueError(
+ "Name {} is invalid. Only letters, numbers, '_', and '-' are permitted ".format(
+ div_id))
+
+ return Template(head + self.template).render(
+ div_id=div_id.replace(" ", "_"),
+ custom_css=custom_css,
+ title=title,
+ y_axis_tick_format=y_axis_tick_format,
+ secondary_y_axis_tick_format=secondary_y_axis_tick_format,
+ data=json.dumps(
+ data, indent=4).replace("'", "\\'").replace('"', "'"))
+
+ def plot_and_save(self,
+ data,
+ w=800,
+ h=430,
+ filename='chart',
+ subplots=False,
+ subplot_groups=False,
+ title=False,
+ overwrite=True):
+ '''
+ save the rendered html to a file and returns an IFrame to display the plot in the notebook
+ '''
+ self.save(data, filename, overwrite,)
+ return IFrame(filename + '.html', w, h)
+
+ def plot(self,
+ data,
+ w=800,
+ h=430,
+ div_id='chart',
+ subplots=False,
+ subplot_groups=False,
+ title=False,
+ **kwargs):
+ '''
+ output an iframe containing the plot in the notebook without saving
+ '''
+
+ if subplots:
+ if title:
+ if len(title) > 0:
+ title = title + '
'
+ body = ''
+
+ if not subplot_groups:
+ subplot_groups = {col: [col] for col in data.columns}
+
+ for group in subplot_groups:
+ body = body + (self.render(data=data[subplot_groups[group]],
+ div_id=str(div_id) + str(group),
+ head=self.head,
+ title=str(title) + str(group),
+ **kwargs
+ )
+ )
+ title=''
+ else:
+ body = self.render(
+ data=data,
+ div_id=div_id,
+ head=self.head,
+ **kwargs)
+
+ return HTML(self.iframe.format(source=body, w=w, h=h*len(subplot_groups)))
+
+ def update():
+ pass
+
+ def save(self, data, filename='chart', overwrite=True):
+ '''
+ save the rendered html to a file in the same directory as the notebook
+ '''
+ try:
+ data = self.pandas_data(data, **kwargs)
+ except AttributeError:
+ pass
+
+ html = self.render(data=data, div_id=filename, head=self.head)
+ if overwrite:
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ if not os.path.exists(filename.replace(" ", "_") + '.html'):
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ raise IOError('File Already Exists!')
+
+ def pandas_data(self,
+ df,
+ colors=False,
+ data_label_formats=False,
+ data_labels=False,
+ grid=False,
+ group=False,
+ height=300,
+ hue=False,
+ kind='line',
+ kinds=None,
+ legend=True,
+ mark_right=False,
+ point=False,
+ secondary_y=list(),
+ stacked=False,
+ subchart=False,
+ subplots=False,
+ tick_count=10,
+ value=False,
+ value_labels=False,
+ x_axis_tick_culling=False,
+ x_axis_type='auto',
+ x_tick_values=False,
+ xlabels=False,
+ xlim=False,
+ xregions=False,
+ xy_rotated=False,
+ ylabels=False,
+ ylim=False,
+ yregions=False,
+ zoom=False,
+ ):
+ '''
+ create data dictionary from pandas DataFrame
+
+ TODO:
+ ## Pandas Features
+ * proper docstring
+ * subplots
+ * layout
+ * height -> figsize
+ * use_index
+ * legend
+ * xlim (axis.x.min, axis.x.max or axis.x.extent)
+ * ylim
+ * colorbar
+ * table
+ * axis-rotation
+ ## Seaborn-esque features
+ * hue - ability to provide long form data
+ ## C3 Features
+ * interaction: {enabled: false}
+ * transition: {duration: 500}
+ * onrendered: function() {...}
+ * onmouseover/out
+ * data.empty.label.text
+ * data.selection.enabled
+ * data.selection.grouped
+ * data.selection.multiple
+ * data.selection.draggable
+ * axis.x.tick.fit
+ * axis.x.tick.values
+ * axis.x.tick.rotate
+ * axis.x.label
+ * axis.x.show
+ * legend.hide
+ * legend.position
+ * tooltip.show
+ * tooltip.grouped
+ * point.focus.expand.enabled
+ * subchart.size.height
+ * point.focus.expand.r
+ * point.select.r
+ * line.connectNull
+
+
+ param kind: str
+ * line
+ * spline
+ * step
+ * areas
+ * area-spline
+ * area-step
+ * bar
+ * scatter
+ * pie
+ * donut
+ * gauge
+
+ param x_axis_type: str
+ * timeseries
+ * category
+ * numeric
+
+ '''
+ # kinds = ['line', 'spline', 'step', 'area','area-spline', 'area-step',
+ # 'bar', 'scatter', 'pie', 'donut', 'gauge']
+
+ data = {
+ 'size': {
+ 'height': height,
+ },
+ "data": {
+ 'x': 'x',
+ 'axes': dict()
+ },
+ 'subchart': {
+ 'show': subchart
+ },
+ 'point': {
+ 'show': point
+ },
+ 'grid': {
+ 'x': {
+ 'show': grid
+ },
+ 'y': {
+ 'show': grid
+ }
+ },
+ 'axis': {
+ 'rotated': xy_rotated,
+ 'x': {'tick': {'count': tick_count,
+ 'values': x_tick_values,
+ 'culling': dict(),
+ },
+ },
+ 'y': {'tick': {'format': ''}},
+ 'y2': {'tick': {}},
+ },
+ 'zoom': {}
+
+ }
+ if kind:
+ data['data']['type'] = kind
+ if kinds:
+ data['data']['types'] = kinds
+
+ if mark_right:
+ df = df.rename(
+ columns={col: col + '(right)' for col in secondary_y})
+ secondary_y = [y + '(right)' for y in secondary_y]
+ if hue and value:
+ df = df.groupby([df.index.name, hue])[value].sum().unstack()
+
+ df = df.copy()
+ df['x'] = df.index
+ df['x'] = df['x'].astype('str').values.tolist()
+
+ data['data']['columns'] = [[col] + df[col].values.tolist()
+ for col in df.columns]
+ # data['data']['columns'].extend([['x'] + df.index.astype('str').values.tolist()])
+ for col in df.columns:
+ if col in secondary_y:
+ data['data']['axes'][col] = 'y2'
+ else:
+ data['data']['axes'][col] = 'y'
+ if len(secondary_y) > 0:
+ data['axis']['y2']['show'] = True
+
+ if colors:
+ # repeat color palette if not long enough
+ colors = colors*math.ceil(len(df.columns)/len(colors))
+ color_data = {}
+ for col, color in zip(df.columns, colors):
+ color_data[col] = color
+ data['data']['colors'] = color_data
+
+ if x_axis_type == 'auto':
+ index_type = str(df.index.dtype)
+
+ if 'date' in index_type:
+ data['axis']['x']['type'] = 'timeseries'
+ data['axis']['x']['tick']['format'] = '%Y-%m-%d'
+
+ if 'object' in index_type or 'category' in index_type:
+ data['axis']['x']['type'] = 'category'
+ data['axis']['x']['tick']['culling'][
+ 'max'] = x_axis_tick_culling
+ else:
+ if 'date' in x_axis_type or 'time' in x_axis_type:
+ data['axis']['x']['type'] = 'timeseries'
+ data['axis']['x']['tick']['format'] = '%Y-%m-%d'
+
+ if 'categor' in x_axis_type or 'str' in x_axis_type:
+ data['axis']['x']['type'] = 'category'
+ data['axis']['x']['tick']['culling'][
+ 'max'] = x_axis_tick_culling
+
+ if xlim:
+ data['axis']['x']['min'] = xlim[0]
+ data['axis']['x']['max'] = xlim[1]
+
+ if ylim:
+ data['axis']['y']['min'] = ylim[0]
+ data['axis']['y']['max'] = ylim[1]
+
+ if stacked:
+ group = df.columns.values.tolist()
+ group.pop(-1)
+ group = [group]
+
+ if group:
+ data['data']['groups'] = group
+
+ if zoom:
+ data['zoom']['enabled'] = True
+ data['zoom']['rescale'] = True
+
+ if xregions:
+ data['regions'] = [{'axis': 'x', 'start': region[
+ 0], 'end':region[1]} for region in xregions]
+
+ if yregions:
+ data['regions'] = [{'axis': 'y', 'start': region[
+ 0], 'end':region[1]} for region in yregions]
+
+ if xlabels:
+ data['grid']['x']['lines'] = [
+ {'value': label[0], 'text': label[1]} for label in xlabels]
+
+ if ylabels:
+ data['grid']['y']['lines'] = [
+ {'value': label[0], 'text': label[1]} for label in ylabels]
+
+ if data_labels:
+ if data_labels == True:
+ data_labels = df.drop('x', axis=1).columns
+ if data_label_formats:
+ data['data']['labels'] = {}
+ for column in data_label_formats:
+ data['data']['labels'][column] = data_label_formats[column]
+ else:
+ data['data']['labels'] = True
+
+ return data
diff --git a/src/iplotter/chartist_plotter.py b/src/iplotter/chartist_plotter.py
new file mode 100644
index 0000000..30d9e92
--- /dev/null
+++ b/src/iplotter/chartist_plotter.py
@@ -0,0 +1,98 @@
+from jinja2 import Template
+from IPython.display import IFrame, HTML
+import os
+import json
+from .base_plotter import IPlotter
+
+
+class ChartistPlotter(IPlotter):
+ """
+ Class for creating chartist.js charts in ipython notebook
+ """
+
+ head = '''
+
+
+
+ '''
+
+ template = '''
+
+
+ '''
+
+ def __init__(self):
+ super(ChartistPlotter, self).__init__()
+
+ def render(self, data, chart_type, options=None, div_id="chart", head=""):
+ '''
+ render the data in HTML template
+ '''
+ if not self.is_valid_name(div_id):
+ raise ValueError(
+ "Name {} is invalid. Only letters, numbers, '_', and '-' are permitted ".format(
+ div_id))
+
+ return Template(head + self.template).render(
+ div_id=div_id.replace(" ", "_"),
+ data=json.dumps(
+ data, indent=4).replace("'", "\\'").replace('"', "'"),
+ chart_type=chart_type,
+ options=json.dumps(
+ options, indent=4).replace("'", "\\'").replace('"', "'"))
+
+ def plot_and_save(self,
+ data,
+ chart_type,
+ options=None,
+ w=800,
+ h=420,
+ filename='chart',
+ overwrite=True):
+ '''
+ save the rendered html to a file and return an IFrame to display the plot in the notebook
+ '''
+ self.save(data, chart_type, options, filename, overwrite)
+ return IFrame(filename + '.html', w, h)
+
+ def plot(self, data, chart_type, options=None, w=800, h=420):
+ '''
+ output an iframe containing the plot in the notebook without saving
+ '''
+ return HTML(
+ self.iframe.format(
+ source=self.render(
+ data=data,
+ options=options,
+ chart_type=chart_type,
+ head=self.head),
+ w=w,
+ h=h))
+
+ def save(self,
+ data,
+ chart_type,
+ options=None,
+ filename='chart',
+ overwrite=True):
+ '''
+ save the rendered html to a file in the same directory as the notebook
+ '''
+ html = self.render(
+ data=data,
+ chart_type=chart_type,
+ options=options,
+ div_id=filename,
+ head=self.head)
+
+ if overwrite:
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ if not os.path.exists(filename.replace(" ", "_") + '.html'):
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ raise IOError('File Already Exists!')
diff --git a/src/iplotter/chartjs_plotter.py b/src/iplotter/chartjs_plotter.py
new file mode 100644
index 0000000..e8e3762
--- /dev/null
+++ b/src/iplotter/chartjs_plotter.py
@@ -0,0 +1,115 @@
+from jinja2 import Template
+from IPython.display import IFrame, HTML
+import os
+import json
+from .base_plotter import IPlotter
+
+
+class ChartJSPlotter(IPlotter):
+ """
+ Class for creating charts.js charts in ipython notebook
+ """
+
+ head = '''
+
+
+ '''
+
+ template = '''
+
+
+ '''
+
+ def __init__(self):
+ super(ChartJSPlotter, self).__init__()
+
+ def render(self,
+ data,
+ chart_type,
+ options=None,
+ div_id="chart",
+ head="",
+ w=800,
+ h=420):
+ '''
+ render the data in HTML template
+ '''
+ if not self.is_valid_name(div_id):
+ raise ValueError(
+ "Name {} is invalid. Only letters, numbers, '_', and '-' are permitted ".format(
+ div_id))
+
+ return Template(head + self.template).render(
+ div_id=div_id.replace(" ", "_"),
+ data=json.dumps(
+ data, indent=4).replace("'", "\\'").replace('"', "'"),
+ chart_type=chart_type,
+ options=json.dumps(
+ options, indent=4).replace("'", "\\'").replace('"', "'"),
+ w=w,
+ h=h)
+
+ def plot_and_save(self,
+ data,
+ chart_type,
+ options=None,
+ w=800,
+ h=420,
+ filename='chart',
+ overwrite=True):
+ '''
+ save the rendered html to a file and return an IFrame to display the plot in the notebook
+ '''
+ self.save(data, chart_type, options, filename, w, h, overwrite)
+ return IFrame(filename + '.html', w, h)
+
+ def plot(self, data, chart_type, options=None, w=800, h=420):
+ '''
+ output an iframe containing the plot in the notebook without saving
+ '''
+ return HTML(
+ self.iframe.format(
+ source=self.render(
+ data=data,
+ chart_type=chart_type,
+ options=options,
+ head=self.head,
+ w=w,
+ h=h),
+ w=w,
+ h=h))
+
+ def save(self,
+ data,
+ chart_type,
+ options=None,
+ filename='chart',
+ w=800,
+ h=420,
+ overwrite=True):
+ '''
+ save the rendered html to a file in the same directory as the notebook
+ '''
+ html = self.render(
+ data=data,
+ chart_type=chart_type,
+ options=options,
+ div_id=filename,
+ head=self.head,
+ w=w,
+ h=h)
+
+ if overwrite:
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ if not os.path.exists(filename.replace(" ", "_") + '.html'):
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ raise IOError('File Already Exists!')
diff --git a/src/iplotter/export.py b/src/iplotter/export.py
new file mode 100644
index 0000000..8c065b7
--- /dev/null
+++ b/src/iplotter/export.py
@@ -0,0 +1,35 @@
+import time
+from selenium import webdriver
+import os
+
+
+class VirtualBrowser(object):
+ """Helper class for converting html charts to png"""
+
+ def __init__(self, driver=webdriver.Chrome):
+ super(VirtualBrowser, self).__init__()
+ self.driver = driver()
+
+ def __enter__(self):
+ return self
+
+ def save_as_png(self, filename, width=300, height=250, render_time=1):
+ '''
+ open saved html file in an virtual browser and save a screen shot to PNG format
+ '''
+ self.driver.set_window_size(width, height)
+ self.driver.get('file://{path}/{filename}'.format(
+ path=os.getcwd(), filename=filename + ".html"))
+ time.sleep(render_time)
+ self.driver.save_screenshot(filename + ".png")
+
+ def __exit__(self, type, value, traceback):
+ self.driver.quit()
+ return True
+
+ def quit(self):
+ '''
+ shutdown virtual browser when finished
+ '''
+ self.driver.quit()
+ return True
\ No newline at end of file
diff --git a/src/iplotter/google_plotter.py b/src/iplotter/google_plotter.py
new file mode 100644
index 0000000..2dcb163
--- /dev/null
+++ b/src/iplotter/google_plotter.py
@@ -0,0 +1,124 @@
+from jinja2 import Template
+from IPython.display import IFrame, HTML
+import os
+import json
+from .base_plotter import IPlotter
+
+
+class GCPlotter(IPlotter):
+ """
+ Class for creating Google Charts in ipython notebook
+ """
+ head = '''
+
+
+ '''
+
+ template = '''
+
+
+ '''
+
+ def __init__(self):
+ super(GCPlotter, self).__init__()
+
+ def render(self,
+ data,
+ chart_type,
+ chart_package='corechart',
+ options=None,
+ div_id="chart",
+ head=""):
+ '''
+ render the data in HTML template
+ '''
+ if not self.is_valid_name(div_id):
+ raise ValueError(
+ "Name {} is invalid. Only letters, numbers, '_', and '-' are permitted ".format(
+ div_id))
+
+ return Template(head + self.template).render(
+ div_id=div_id.replace(" ", "_"),
+ data=json.dumps(
+ data, indent=4).replace("'", "\\'").replace('"', "'"),
+ chart_type=chart_type,
+ chart_package=chart_package,
+ options=json.dumps(
+ options, indent=4).replace("'", "\\'").replace('"', "'"))
+
+ def plot_and_save(self,
+ data,
+ chart_type,
+ chart_package='corechart',
+ options=None,
+ w=800,
+ h=420,
+ filename='chart',
+ overwrite=True):
+ '''
+ save the rendered html to a file and return an IFrame to display the plot in the notebook
+ '''
+ self.save(data, chart_type, chart_package, options, filename,
+ overwrite)
+ return IFrame(filename + '.html', w, h)
+
+ def plot(self,
+ data,
+ chart_type,
+ chart_package='corechart',
+ options=None,
+ w=800,
+ h=420):
+ '''
+ output an iframe containing the plot in the notebook without saving
+ '''
+ return HTML(
+ self.iframe.format(
+ source=self.render(
+ data=data,
+ options=options,
+ chart_type=chart_type,
+ chart_package=chart_package,
+ head=self.head),
+ w=w,
+ h=h))
+
+ def save(self,
+ data,
+ chart_type,
+ chart_package='corechart',
+ options=None,
+ filename='chart',
+ overwrite=True):
+ '''
+ save the rendered html to a file in the same directory as the notebook
+ '''
+ html = self.render(
+ data=data,
+ chart_type=chart_type,
+ chart_package=chart_package,
+ options=options,
+ div_id=filename,
+ head=self.head)
+
+ if overwrite:
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ if not os.path.exists(filename.replace(" ", "_") + '.html'):
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ raise IOError('File Already Exists!')
diff --git a/src/iplotter/plotly_plotter.py b/src/iplotter/plotly_plotter.py
new file mode 100644
index 0000000..dec9677
--- /dev/null
+++ b/src/iplotter/plotly_plotter.py
@@ -0,0 +1,88 @@
+from jinja2 import Template
+from IPython.display import IFrame, HTML
+import os
+import json
+from .base_plotter import IPlotter
+
+
+class PlotlyPlotter(IPlotter):
+ """
+ Class for creating plotly.js charts in ipython notebook
+ """
+
+ head = '''
+
+
+
+
+ '''
+
+ template = '''
+
+
+ '''
+
+ def __init__(self):
+ super(PlotlyPlotter, self).__init__()
+
+ def render(self, data, layout=None, div_id="chart", head=""):
+ '''
+ render the data in HTML template
+ '''
+ if not self.is_valid_name(div_id):
+ raise ValueError(
+ "Name {} is invalid. Only letters, numbers, '_', and '-' are permitted ".format(
+ div_id))
+
+ return Template(head + self.template).render(
+ div_id=div_id.replace(" ", "_"),
+ data=json.dumps(
+ data, indent=4).replace("'", "\\'").replace('"', "'"),
+ layout=json.dumps(
+ layout, indent=4).replace("'", "\\'").replace('"', "'"))
+
+ def plot_and_save(self,
+ data,
+ layout=None,
+ w=800,
+ h=420,
+ filename='chart',
+ overwrite=True):
+ '''
+ save the rendered html to a file and return an IFrame to display the plot in the notebook
+ '''
+ self.save(data, layout, filename, overwrite)
+ return IFrame(filename + '.html', w, h)
+
+ def plot(self, data, layout=None, w=800, h=420):
+ '''
+ output an iframe containing the plot in the notebook without saving
+ '''
+ return HTML(
+ self.iframe.format(
+ source=self.render(
+ data=data,
+ layout=layout,
+ head=self.head, ),
+ w=w,
+ h=h))
+
+ def save(self, data, layout=None, filename='chart', overwrite=True):
+ '''
+ save the rendered html to a file in the same directory as the notebook
+ '''
+ html = self.render(
+ data=data, layout=layout, div_id=filename, head=self.head)
+
+ if overwrite:
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ if not os.path.exists(filename.replace(" ", "_") + '.html'):
+ with open(filename.replace(" ", "_") + '.html', 'w') as f:
+ f.write(html)
+ else:
+ raise IOError('File Already Exists!')