Out Google Charts, in amCharts

I’ve posted statistics about tweeting companies for some time now and with the latest refresh, I changed the presentation to amCharts charting library from Google Charts library.

While Google Charts is very good charting library, there was one major reason to move away from it: I wanted to host the script library in my own site.

With that in mind, I started to investigate various charting libraries. Wikipedia has good comparison about charting libraries, and based on that I started my investigation.

As I looked at web sites of various charting libraries, I focused on the following:

  • Examples, what charts were available.
  • Code, how the charts were made and apparent easiness of the code.
  • Documentation, examples are the starting point but good documentation is a must to fine tune any charts.
  • Library must be free to use.

amCharts web site presents some charts right there in their landing page and they are good looking charts so I decided to look more closely at amCharts. Their samples site show lots of different charts that can be made using amCharts and looking at, for example, piechart sample code, I saw that the code was straight-forward and easy to understand. The documentation is clear and amCharts is free to use. Being free was “the final nail in the coffin” (in a positive sense :-), so I decided to download amCharts and start making the new charts.

I made an application that generates the tweeting companies statistics using Python. I thought to increase reusability and divided functionality such as generating charts and accessing database to different scripts (it’s a good idea, by the way :-). The main Python script is executed weekly using cron-job and it posts new statistics to WordPress using XML-RPC. Tweets are retrieved daily by another cron-job and saved to MySQL database. I used python-twitter API to access Twitter and python-wordpress-xmlrpc to connect to WordPress. The featured image is not done using Python but Python code executes gnuplot, a command-line driven graphing utility, that generates the chart.

Here are some code excerpts from the application.

def executeGnuplot():
  accountIndex=0
  index=0
  topTweeters=[]

  for account in sortedAccountClasses:
    if index<10 and account.tweets>0 and account.twoWeekTweets>0:
      topTweeters.append(account.name)
      index=index+1
  colors=["gold","slategrey","brown","violet","navy","aquamarine","green","blue","red","orchid4"]
  datFile=open(tweetingCompaniesDatFile,"w")
  index=1
  for accountName in topTweeters:
    print >> datFile, "%d %d %s %s" % (index,accountClasses[accountName].tweets,colors[index-1],accountName)
    index=index+1
  datFile.close()

  gnuplotFile=open(histogramGnuFile,"w")

  print >> gnuplotFile, 'set terminal pngcairo notransparent enhanced size 1038, 500'
  print >> gnuplotFile, 'set output "%s"' % histogramPngFile
  print >> gnuplotFile, 'set style fill solid 2.00 border lt -1'
  print >> gnuplotFile, 'set nokey'
  print >> gnuplotFile, 'set logscale y'
  print >> gnuplotFile, 'set ylabel "Tweets"'
  print >> gnuplotFile, 'set title "Tweeting companies %s"' % weekStringTitle
  print >> gnuplotFile, 'unset xtics'
  print >> gnuplotFile, 'set x2tics scale 0 rotate by 30'
  print >> gnuplotFile, 'set lmargin at screen 0.15'
  print >> gnuplotFile, 'set rmargin at screen 0.85'
  print >> gnuplotFile, 'set bmargin at screen 0.20'
  print >> gnuplotFile, 'plot for [color in "%s"] "%s" using 1:(strcol(3) eq color ? $2:NaN):(0.45):x2tic(4) with boxes lc rgb color axes x2y1' % (" ".join(colors),tweetingCompaniesDatFile)

  gnuplotFile.close()

  #call gnuplot
  gnuplotArgs=['gnuplot',histogramGnuFile]
  rv=subprocess.call(gnuplotArgs)
  if rv==0:
    return histogramPngFile
  else:
    return None
set terminal pngcairo notransparent enhanced size 1038, 500
set output "statfiles/1601-22012014_histogram.png"
set style fill solid 2.00 border lt -1
set nokey
set logscale y
set ylabel "Tweets"
set title "Tweeting companies 16.1 - 22.1.2014"
unset xtics
set x2tics scale 0 rotate by 30
set lmargin at screen 0.15
set rmargin at screen 0.85
set bmargin at screen 0.20
#Changing colors: http://stackoverflow.com/a/15810670
plot for [color in "gold slategrey brown violet navy aquamarine green blue red orchid4"] "statfiles/1601-22012014_tweetingcompanies.dat" using 1:(strcol(3) eq color ? $2:NaN):(0.45):x2tic(4) with boxes lc rgb color axes x2y1
1 6 gold upm_news
2 11 slategrey utc
3 55 brown bofa_news
4 51 violet jnjnews
5 8 navy outotec
6 18 aquamarine nesteoilgroup
7 40 green op_pohjola
8 202 blue hp
9 72 red nsntweets
10 2 orchid4 ruukki
  def piechartScript(self,accountList,titleList,divId,hideLabelPercent=2):

    content=""
    content+='<script type="text/javascript">'
    content+='var c_%s;' % divId #differentiate variable names

    content+='var chartData_%s = [' % divId
    for account in accountList:
      content+='{'
      content+='"a":"%s",' % account.name
      content+='"t":%d' % account.tweets
      content+='},'

    content=content[:-1]#removes extra comma from data lsit
    content+='];'

    content+='AmCharts.ready(function () {'
    # // PIE CHART
    content+='c_%s = new AmCharts.AmPieChart();' % divId
    for title in titleList:
      content+='c_%s.addTitle("%s",14,"#000000#",1,true);' % (divId,title)

    content+='c_%s.dataProvider = chartData_%s;' % (divId,divId)
    content+='c_%s.titleField = "a";' % divId
    content+='c_%s.valueField = "t";' % divId
    content+='c_%s.hideLabelsPercent = %d;' % (divId,hideLabelPercent)
    content+='c_%s.labelRadius = 1;' % divId
    content+='c_%s.radius="25%%";'  % divId
    content+='c_%s.fontSize=10;'  % divId
    content+='c_%s.outlineColor = "#FFFFFF";' % divId
    content+='c_%s.outlineAlpha = 0.8;' % divId
    content+='c_%s.outlineThickness = 1;' % divId
    content+='c_%s.balloonText = "[[title]]<br/><span style=\'font-size:14px\'><b>[[value]]</b> ([[percents]]%%)</span>";' % divId

    content+=self.addChartFunctionality(divId,legend=False,cursor=False,scrollbar=False)
    content+='c_%s.write("%s");' % (divId,divId)
    content+='});'

    content+="</script>"

    return content
def publishToBlog(content,featuredImage):
  server="http://server.url/xmlrpc.php"
  user="username"
  password="password"

  publish=config.publishToBlog

  wordpress = Client(server,user,password)

  attachmentId=None
  if featuredImage is not None:
    print "featuredImage: ",featuredImage
    # prepare metadata
    data = {
        'name': histogramPngFileName,
        'type': 'image/png',  # mimetype
    }
    # read the binary file and let the XMLRPC library encode it into base64
    with open(histogramPngFile, 'rb') as img:
      data['bits'] = xmlrpc_client.Binary(img.read())
    response = wordpress.call(media.UploadFile(data))
    attachmentId = response['id']
  post = WordPressPost()
  post.title = 'Tweeting companies '+ utils.getWeekString()
  post.content = content
  termsNames=dict()
  termsNames['post_tag']=['TweetStats_v4']
  termsNames['category']=['Visualization']

  post.terms_names=termsNames
  if attachmentId is not None:
    #set featured image
    post.thumbnail=attachmentId

  if(publish):
    post.post_status = 'publish'

  postId=wordpress.call(NewPost(post))
  print "Tweet stats posted. ID: %s" % postId

Only thing that I found lacking in amCharts is that it doesn’t have a table chart (Google Charts does have a table chart). But then again, table is not really a chart, so it’s understandable that amCharts doesn’t have it.

So I had to find a library that does tables and especially table sorting. After looking into a few table libraries, I chose table.js from Matt Kruse’s Javascript Toolbox. It may not have all the features from other table libraries but it’s small, simple to use and does what I want.
Here’s an example of sortable table (click column headers to sort).

Accounts
Account Sector Industry Tweets
3mnews Industrials Industrial Conglomerates 27
abbgroupnews Industrials Heavy Electrical Equipment 98

And here’s the code. No Javascript required.

<table class="example table-autosort:0 table-stripeclass:alternate">
  <thead>
    <tr>
      <th colspan="4">Accounts</th>
    </tr>
    <tr>
      <th class="table-sortable:default">Account</th>
      <th class="table-sortable:default">Sector</th>
      <th class="table-sortable:default">Industry</th>
      <th class="table-sortable:numeric">Tweets</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>3mnews</td>
      <td>Industrials</td>
      <td>Industrial Conglomerates</td>
      <td>27</td>
    </tr>
    <tr>
      <td>abbgroupnews</td>
      <td>Industrials</td>
      <td>Heavy Electrical Equipment</td>
      <td>98</td>
    </tr>
  </tbody>
</table>

Leave a Reply

Your email address will not be published. Required fields are marked *