Hitachi Vantara Pentaho Community Forums
Results 1 to 2 of 2

Thread: X-axis using logorithm scale

  1. #1

    Question X-axis using logorithm scale

    Hi All,

    I am wondering if there is a way to change the scale of the x-axis to be logarithm instead of linear, it appear to be doable in protovis
    var yScale = pv.Scale.log(0,popCountArray.length).range(0,height);
    I have a dot chart that would like the x-axis ticks to be 0, 100, 10000, 1000000, 10000000,
    instead of 0, 2000000,4000000,6000000,8000000, 10000000

    If it is doable, where and how can I do it?

    Thanks.

  2. #2
    Join Date
    Mar 2012
    Posts
    645

    Default

    Not doable solely with simple configuration, in CCC, whether for the X or the Y axis.

    The workaround is to simulate the log scale, by providing pre-log'ed values to the chart.
    Provide the X-values as the logarithm of base 10 of the real value:
    • -1 <- 10^-1 = 0.1
    • 0 <- 10^0 = 1
    • 1 <- 10^1 = 10
    • 2 <- 10^2 = 100
    • 3 <- 10^3 = 1000
    • ...


    Assuming we're using a metric chart, the X-axis dimension is named "x", by default (if not a metric chart, note that, in most other charts, the X-axis is fed with a dimension named "category").

    The conversion can be done by specifying a "converter" for the "x" dimension.
    Then, format the dimension so that the original value is always shown to the user, instead of the exponent.
    The "x" dimension's "formatter" handles the value that is shown in the tooltip.
    For when the value is shown in the X-axis' ticks, specify the "baseAxisTickFormatter" property.

    In the component's "postFetch" handler specify:
    Code:
    function() {
        // Protovis formatter for the original values (you may need to adjust the number of decimal places)
        var rawFormatter = pv.Format.createFormatter(pv.Format.number().fractionDigits(0, 2));
        
        var cd = this.chartDefinition;
        cd.dimensions = {
             "x": {
                 // Convert X Values to its log_10.
                 // Original value, v, remains accessible in datum.rawValue .
                 // Negative values are converted to null (they're not representable with log scale).
                 converter: function(v) {
                        if(v != null) { v = +v; } // Convert non-null to number
                        return (v == null || isNaN(v) || v <= 0) ? null : (Math.log(v)/Math.LN10);
                 },
    
                 // The original/raw value is shown, formatted:
                 formatter: function(value, rawValue) { 
                     return value == null ? "-" : rawFormatter(+rawValue);
                 }
             }
        };
        
        // Calculate the corresponding original value and format it.
        // You could also show it as 10^-1, 10^0, 10^1, ... or some variation of this.
        cd.baseAxisTickFormatter = function(v) {
            return rawFormatter(Math.pow(10, v));
        };
        
        // You may want to provide an adequate axis title. Something like:
        cd.baseAxisTitle = "Frequency (log 10)";
    
        // Show grid lines
        cd.baseAxisGrid = true;
        
        // Force tick and grid step to be 1.
        //   Hack 1. Ensure only 1x, 2x, 5x ticks steps are generated:
        cd.baseAxisTickExponentMin = 0;
        cd.baseAxisTickExponentMax = 0;
    
        //   Hack 2. Exclude 2x and 5x steps unless it gets really clobbered.
        cd.baseAxisLabelSpacingMin = 0.1; // em.s
    
        //   Hack 3. Force ticks to align with plot ends.
        cd.baseAxisDomainRoundMode = 'tick';
        
        // Hide minor ticks, cause these are drawn midway between major ticks, and not at the correct 5*10^n position...
        cd.baseAxisMinorTicks = false;
    }
    If you really wan't to show only the ticks and grid-lines of 1, 100, 10000, etc, you'll need to hide every other tick and grid-line, by specifying the following extension points:
    • orthoAxisTicksPanel_visible --> function() { return !(this.index % 2); }
    • orthoAxisGrid_visible --> function() { return !(this.index % 2); }

    Last but not the least, if you wan't to show gridlines in the 2*10^n, 3*10^n, ... between the major grid-lines, you'll have a little more work.
    Place the following in the "orthoAxisGrid_add" extension point:
    Code:
    function() {
        return new pv.Rule()
            .data(function() {
                var data = this.delegate();
                var minorsRange = pv.range(2, 10, 1); // 2, 3, ..., 9
                var minorScenes = [];
                data.forEach(function(majorScene, i) {
                    // Skip last major tick. Don't draw outside the plot.
                    if(i === data.length - 1) { return; }
                    
                    var v_major = majorScene.vars.tick.value;
                    var v_origMajor = Math.pow(10, v_major);
                    minorsRange.forEach(function(m) {
                        var orig_m = m * v_origMajor;
                        var v_minor = Math.log(orig_m) / Math.LN10;
                        var minorScene = new pvc.visual.CartesianAxisTickScene(majorScene, {
                            tick:      v_minor,
                            tickRaw:   v_minor,
                            tickLabel: v_minor.toFixed(2) // not really relevant
                        });
                        minorScenes.push(minorScene);
                    });
                });
                return minorScenes;
            })
            .zOrder(-12); // Not inherited from major grid-rule. Must repeat.
    }
    I've set a up a jsFiddle showing this technique, here.
    Hope it helps! In a not so distant future, we'll surely add support for log scales, in a more yes/no fashion...

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Privacy Policy | Legal Notices | Safe Harbor Privacy Policy

Copyright © 2005 - 2019 Hitachi Vantara Corporation. All Rights Reserved.