Scatter Chart란, 분산/분포도를 표현해주는 차트입니다.

    분포되는 그래프란 아래와 같은 차트를 의미합니다. ▼




    [출처 : http://dev.sencha.com/ext/5.1.0/examples/kitchensink/?charts=true#scatter-basic]



    [출처 : http://www.mathsisfun.com/data/scatter-xy-plots.html]



    차트 영역내에 포인트로 데이터를 표현하여 어느 영역에 집중이 되있는지 한눈에 알아보기 쉬운차트가 바로 'Scatter Chart' 입니다.


    상단 이미지 중, 두번째 출처에서 제공하는 온도별 아이스크림 판매 데이터를 이용하여 한번 Scatter Chart를 생성해보도록 하겠습니다.





    상단 데이터를 데이터 스토어에 담아 보도록 합니다. ▼

    
    	var store = Ext.create('Ext.data.Store',{
    				   	 fields: ['temp', 'price' ],
    				     data: [
    				         { temp: 14.2, price: 215 },
    				         { temp: 16.4, price: 325 },
    				         { temp: 11.9, price: 185 },
    				         { temp: 15.2, price: 332 },
    				         { temp: 18.5, price: 406 },
    				         { temp: 22.1, price: 522 },
    				         { temp: 19.4, price: 412 },
    				         { temp: 25.1, price: 614 },
    				         { temp: 23.4, price: 544 },
    				         { temp: 18.1, price: 421 },
    				         { temp: 22.6, price: 445 },
    				         { temp: 17.2, price: 408 }
    				     ]
    				});
    
    


    Ext.onReady 내에 상단 코드를 정의 하셨다면, 패널 컴포넌트 하위로 차트컴포넌트를 넣어주도록 하겠습니다. ▼

    
    	Ext.create('Ext.Panel',{
    		renderTo : Ext.getBody(),
    		title : 'Scatter Chart',
    		width : 540,
    		height : 400,
    		layout : 'fit',
    		items: [{
    		        xtype: 'cartesian',
    		        height: 500,
    		        insetPadding: 40,
    		        store: store,
    		        axes: [{
    	                    type: 'numeric',
    	                    position: 'bottom',
    	                    fields: ['temp'],
    	                    majorTickSteps : 7,
    	                    maximum : 26,
    	                    minimum : 10,
    	                    grid: true
    	                }, {
    	                   type: 'numeric',
    	                   position: 'left',
    	                   fields: ['price'],
    	                   maximum : 700,
    	                   minimum : 0,
    	                   majorTickSteps : 7,
    	                   grid: true
    	               }],
    	               series: [{
    	                  type: 'scatter',
    	                  xField: 'temp',
    	                  yField: 'price',
    	               }]
    	     }]
    	})
    
    

    이전에는 axes 하위요소들의 type이 numeric 과 category의 조합으로 되어있었지만, 이번에는 모두 x/y 타입을 numeric 으로 하였습니다.

    물론, 항상 numeric 타입으로 정의가 되어야 하는것은 결코 아닙니다.

    이번 케이스의 경우, 온도와 가격이라는 값이 모두 숫자값이어서 numeric으로 설정을 해준 것입니다.

    역시 기본적인 패키지형식으로 axes config, series config 세트로 정의가 되어있으며, series → type이 scatter로 설정하였습니다. 


    실행결과를 보도록 하겠습니다. ▼






    그래프에서도 나타나다시피, 기온이 높아질수록, 아이스크림의 가격은 높아지는 결과가 나타났습니다.

    그렇다면, X/Y 축을 나타내는 text 를 렌더링 및 그래프 규격을 새로 적용해보도록 하겠습니다. ▼

    
    	Ext.create('Ext.Panel',{
    		renderTo : Ext.getBody(),
    		title : 'Scatter Chart',
    		width : 540,
    		height : 400,
    		layout : 'fit',
    		 items: [{
    		        xtype: 'cartesian',
    		        height: 500,
    		        insetPadding: 40,
    		        store: store,
    		        axes: [{
    	                type: 'numeric',
    	                position: 'bottom',
    	                fields: ['temp'],
    	                //add
    	                majorTickSteps : 4,
    	                //add
    	                maximum : 30,
    	                //add
    	                minimum : 10,
    	                //add
    	                increment : 5,
    	                grid: true,
    	                //add
    	                renderer: function (v, layoutContext) {
    	                    return v + "º";
    	                }
    	            }, {
    	                type: 'numeric',
    	                position: 'left',
    	                fields: ['price'],
    	                //add
    	                maximum : 700,
    	                //add
    	                minimum : 0,
    	                //add
    	                majorTickSteps : 7,
    	                grid: true,
    	                //add
    	                renderer: function (v, layoutContext) {
    	                    return '$'+v;
    	                }
    	            }],
    	            series: [{
    	                type: 'scatter',
    	                xField: 'temp',
    	                yField: 'price'
    	            }]
    		    }]
    	})
    
    

    주석으로 add / change 부분이 새로 추가 및 변경된 부분입니다.


    mininum/maximum config을 통하여 데이터의 최소/최대값을 줄여주었습니다.


    그리고 majorTickSteps를 정해주어 간격들을 새로 정의해주었으며, 


    increment를 정해준 이유는, 소수점으로 X축이 나타나는것을 방지하기 위해 최소값부터 5씩 증가하도록 설정을 잡아주었습니다.


    마지막으로, renderer config를 통하여 X/Y의 표출되는 간격값을 렌더링하여 새로운 값(?) 으로 표출 시켜주었습니다. ▼




    더 깔끔한 화면으로 차트가 변경된 것을 확인할 수 있었습니다.


    추가로 ExtJS 5.x 버전부터 scatter chart에서 Ext.draw 클래스를 이용하여 버블차트로 변형이 가능하게끔 되었습니다.

    렌더링 되기전, 기존 마커에 효과를 적용하여 렌더링 해주는 형식으로 재탄생 시키는 것입니다.

    상단 이미지를 제작한 결과물에 추가코드를 적용하여 버블차트를 구현해보도록 하겠습니다. ▼

    
    Ext.onReady(function(){
    	var fromHSL, toHSL, seed = 1.3;
    	
    	Ext.create('Ext.Panel', {
    		renderTo : Ext.getBody(),
            layout: 'fit',
            width: 650,
            items: [{
                xtype: 'cartesian',
                width: '100%',
                height: 500,
                store: Ext.create('Ext.data.Store',{
                	fields: [ 'temp', 'price' ],
                	data :  [
            		         { temp: 14.2, price: 215 },
            		         { temp: 16.4, price: 325 },
            		         { temp: 11.9, price: 185 },
            		         { temp: 15.2, price: 332 },
            		         { temp: 18.5, price: 406 },
            		         { temp: 22.1, price: 522 },
            		         { temp: 19.4, price: 412 },
            		         { temp: 25.1, price: 614 },
            		         { temp: 23.4, price: 544 },
            		         { temp: 18.1, price: 421 },
            		         { temp: 22.6, price: 445 },
            		         { temp: 17.2, price: 408 }
            		     ]
                }),
                insetPadding: 20,
                interactions: ['panzoom', 'itemhighlight'],
                series: {
                    type: 'scatter',
                    xField: 'temp',
                    yField: 'price',
                    highlightCfg: {
                        scale: 2
                    },
                    style: {
                        renderer: function (sprite, config, rendererData, index) {
                            var store = rendererData.store,
                                storeItem = store.getData().items[index];
                            config.radius = interpolate(storeItem.data.price, 0, 1000, 5, 30);
                            config.fillOpacity = interpolate(storeItem.data.price, 0, 1000, 1, 0.7);
                            config.fill = interpolateColor(storeItem.data.price, 0, 1000);
                            config.stroke = config.fill;
                            config.lineWidth = 3;
                        }
                    }
                },
                axes: [{
                    type: 'numeric',
                    position: 'left',
                    fields: ['price'],
                    //add
                    maximum : 700,
                    //add
                    minimum : 0,
                    //add
                    majorTickSteps : 7,
                    grid : true,
                    //add
                    renderer: function (v, layoutContext) {
                        return '$'+v;
                    }
                }, {
                    type: 'numeric',
                    position: 'bottom',
                    fields: ['temp'],
                  	//add
                    majorTickSteps : 4,
                    //add
                    maximum : 30,
                    //add
                    minimum : 10,
                    //add
                    increment : 5,
                    grid: true,
                    //add
                    renderer: function (v, layoutContext) {
                        return v + "º";
                    }
                }]
            }],
            listeners : {
    			beforerender : function (obj, eOpts) {
    	            fromHSL = Ext.draw.Color.fly('blue').getHSL();
    	            toHSL = Ext.draw.Color.fly('red').getHSL();
    	            fromHSL[2] = 0.3;
    			}
        	}
        });
    	function interpolate(lambda, minSrc, maxSrc, minDst, maxDst) {
    	    return minDst + (maxDst - minDst) * Math.max(0, Math.min(1, (lambda - minSrc) / (maxSrc - minSrc)));
    	}
    
    	function interpolateColor(lambda, minSrc, maxSrc) {
    	    return Ext.draw.Color.fly(0, 0, 0, 0).setHSL(
    	        interpolate(lambda, minSrc, maxSrc, fromHSL[0], toHSL[0]),
    	        interpolate(lambda, minSrc, maxSrc, fromHSL[1], toHSL[1]),
    	        interpolate(lambda, minSrc, maxSrc, fromHSL[2], toHSL[2])
    	    ).toString();
    	}
    })
    
    


    실행결과는 아래와 같습니다. 

    아래와 같은 그림의 유형이라면, 해당 기능을 응용하여 지도를 화면에 출력 후, 인구 밀도 또는 특정 질병에 대한 발생분포에 대해서 표현이 가능할거 같군요.▼





    지금까지 scatter chart에 대해서 알아보았습니다.

    분산 및 분포관련한 작업일 경우 Scatter 차트를 이용해보세요. 









    Posted by 몽고