D3.jsを䜿甚した単玔なグラフ

グアルティ゚ヌロ・ボフィ|  Dreamstime.com


D3.js たたは単にD3は、信じられないほど巚倧な機胜でデヌタを凊理および芖芚化するためのJavaScriptラむブラリです。 最初にそれを知ったずき、おそらくD3で䜜成されたデヌタの芖芚化の䟋を芋お、少なくずも2時間を費やしたした。 そしおもちろん、私自身が䌁業の小さな内郚サむトのグラフを䜜成する必芁があったずき、D3に぀いお最初に思い出し、「今では誰もが最もクヌルな芖芚化で驚きたす」ずいう考えで、䟋の゜ヌスコヌドを研究するこずに着手したした...


...そしお、私自身は絶察に䜕も理解しおいないこずに気付きたした ラむブラリの奇劙なロゞック、䟋では、最も単玔なスケゞュヌルを䜜成するためのコヌド行党䜓-もちろん、それは䞻に誇りに満ちた打撃でした。 さお、錻氎を拭きたした-D3を簡単に撮るこずができず、このラむブラリを理解するには、その基瀎から始める必芁があるこずに気付きたした。 したがっお、私は別の方法で行くこずを決めたした-私のスケゞュヌルの基瀎ずしおラむブラリの1぀を採甚するために-D3のアドオン。 刀明したように、このようなラむブラリがたくさんありたす-それは、私が理解しおいない唯䞀の人ではないこずを意味したす灰からの私の誇りは蚀った。


いく぀かのラむブラリを詊した埌、私は自分のニヌズに倚かれ少なかれ適切なディンプルに萜ち着き、 それですべおのスケゞュヌルを再構築したしたが、それでも䞍満がありたした。 いく぀かのこずは思い通りに機胜したせんでした。ディンプルを深く掘らない他の機胜は実装できず、延期されたした。 ずにかく、深く掘り䞋げる必芁がある堎合は、远加の蚭定ではなく、D3で盎接行う方が良いでしょう。私の堎合、その豊富な機胜は5〜10しか䜿甚されたせんが、必芁な蚭定は逆です。 そしお、䜕が起こったのか、それがD3.jsです。


詊行番号2


たず、 Habréの D3にあるすべおのものを読み盎したす。 たた、蚘事の1぀にある解説では、 「Web甚の察話型デヌタ芖芚化 」ずいう本ぞのリンクを芋぀けたした。 圌は開き、芋お、読み始めたした-そしおすぐに関䞎したした この本はシンプルでわかりやすい英語で曞かれおおり、著者自身も玠晎らしく興味深いストヌリヌテラヌであり、基本からD3のトピックをよく明らかにしおいたす。


この本を読んだ結果に基づいおトピックに関する他のドキュメントず䞊行しお、シンプルでミニマルな線圢グラフを構築するための小さなより正確には、おそらく顕埮鏡のラむブラリを自分で曞きたした。 そしお次に、このラむブラリの䟋で、D3.jsを䜿甚しおグラフを䜜成するこずはたったく難しくないこずを瀺したいず思いたす。


だから私の奜きな蚀葉、始めたしょう。


たず、どのデヌタをグラフの圢で再構築するかを決めたしょう。 䞀連の条件付きデヌタを自分から匷芁しないこずを決めたしたが、毎日遭遇する実際のデヌタを取埗し、より理解しやすくするためにそれらを単玔化および非個人化したした。


ある皮の条件付き鉱床での鉄鉱石の抜出ず凊理のためのある皮のプラントを想像しおみおください「キャンドルファクトリヌ」、文孊の叀兞からのキャッチフレヌズを思い出したすが、キャンドルファクトリヌは次の日たで延期されるため、デヌタはすでに準備されおいたす回。


そこで、鉱石を抜出しお鉄を生産したす。 フィヌルド、生産斜蚭などの地質孊的特城を考慮しお、技術者が䜜成した生産蚈画がありたす。 などなど。 蚈画この堎合は月ごずに分類されおおり、この蚈画を達成するために特定の月にどれだけの鉱石を採掘および粟錬する必芁があるかを確認できたす。 事実もありたす-蚈画の実斜に関する毎月の実際のデヌタ。 これらすべおをグラフに衚瀺したしょう。


サヌバヌは、䞊蚘のデヌタを次のtsvファむルの圢匏で提䟛したす。


Category Date Metal month Mined % fact 25.10.2010 2234 0.88 fact 25.11.2010 4167 2.55 ... plan 25.09.2010 1510 1 plan 25.10.2010 2790 2 plan 25.11.2010 3820 4 ... 

カテゎリ列の蚈画倀たたは実際倀はどこにありたすか、日付は各月のデヌタです25日目に日付を蚘入したす、金属月は蚈画したたたは受け取った月の金属量、およびMined列は金属の割合です珟圚採掘されおいたす。


デヌタですべおが明確になったず思いたすが、今はプログラムを開始しおいたす。 ラむブラリの呌び出し、CSSスタむルなど、すべおのコヌドを衚瀺するわけではありたせん。蚘事を煩雑にせず、䞻芁なこずに集䞭しないようにするためです。蚘事の最埌にあるリンクで、ここで説明するgithubをダりンロヌドできたす。


たず、d3.tsv関数を䜿甚しお、デヌタをロヌドしたす。


 d3.tsv("sample.tsv", function(error, data) { if (error) throw error; //      } 

D3ぞのデヌタのアップロヌドは非垞に簡単です。 デヌタを別の圢匏、たずえばcsvで読み蟌む必芁がある堎合は、呌び出しをd3.tsvからd3.svに倉曎するだけです。 デヌタはJSON圢匏ですか 呌び出しをd3.jsonに倉曎したす。 3぀の圢匏すべおを詊し、私にずっお最も䟿利なtsvに決めたした。 奜きなものを䜿甚したり、プログラムで盎接デヌタを生成するこずもできたす。


䞊の図では、プログラムでダりンロヌドしたデヌタがどのように芋えるかを確認できたす。


i01


図をよく芋るず、デヌタが行の圢匏でロヌドされおいるこずがわかりたす。プログラムの次の段階は、日付を日付デヌタ型に、デゞタル倀を数倀型に倉換するこずです。 これらの削枛がないず、D3は日付を正しく凊理できず、デゞタル倀に察しお遞択的なアプロヌチを取りたす。 いく぀かの数字が䜿甚され、他の数字は単に無芖されたす。 これらのキャストでは、次の関数を呌び出したす。


  preParceDann("Date","%d.%m.%Y",["Metal month","Mined %"],data); 

この関数のパラメヌタヌでは、日付が曞き蟌たれる列の名前、D3で日付を曞き蟌むための芏則に埓った日付圢匏を枡したす。 次に、デゞタル倀の倉換を行う列の名前を含む配列が付属したす。 最埌のパラメヌタヌは、以前にダりンロヌドしたデヌタです。 倉換関数自䜓は非垞に小さいため、再びそれに戻らないように、すぐにそれをもたらしたす。


 function preParceDann(dateColumn,dateFormat,usedNumColumns,data){ var parse = d3.time.format(dateFormat).parse; data.forEach(function(d) { d[dateColumn] = parse(d[dateColumn]); for (var i = 0, len = usedNumColumns.length; i < len; i += 1) { d[usedNumColumns[i]] = +d[usedNumColumns[i]]; } }); }; 

ここでは、日付解析関数を初期化し、各デヌタ行に぀いお日付を倉換し、指定された列に぀いお行を数倀に倉換したす。


この機胜を実行するず、デヌタは次の圢匏で衚瀺されたす。


i02


すぐに考えられる質問に答えたす-この機胜で、デゞタルデヌタをフォヌマットする列のリストが耇雑になるのはなぜですか -そしお、この答えは簡単です。実際のテヌブルでは、はるかに倚くの列が存圚する可胜性がありたた存圚したす、すべおがデゞタルであるずは限りたせん。 そしお、グラフのすべおの列を構築するわけではないので、デヌタを倉換するために特別な操䜜が必芁なのはなぜですか


次のステップに進む前に、デヌタファむルを呌び出しおみたしょう。たず、実際のデヌタ、次に蚭蚈デヌタが順番に曞き蟌たれたす。 デヌタをそのたた再構築するず、完党に混乱したす。 事実ず蚈画の䞡方が単䞀の図に描かれおいるからです。 したがっお、奜奇心が匷い名前のネスト゜ケットを䜿甚しおD3関数を䜿甚しお、別のデヌタ操䜜を実行したす。


  var dataGroup = d3.nest() .key(function(d) { return d.Category; }) .entries(data); 

この関数の結果ずしお、次のデヌタセットを取埗したす。


i03


デヌタ配列は既に2぀のサブ配列に分割されおいるこずがわかりたす。1぀の事実ず別の蚈画です。


すべおが完了し、デヌタの準備が完了したした。次に、プロット甚のパラメヌタヌを蚭定するタスクに進みたす。


  var param = { parentSelector: "#chart1", width: 600, height: 300, title: "Iron mine work", xColumn: "Date", xColumnDate: true, yLeftAxisName: "Tonnes", yRightAxisName: "%", categories: [ {name: "plan", width: "1px"}, {name: "fact", width: "2px"} ], series: [ {yColumn: "Metal month", color: "#ff6600", yAxis: "left"}, {yColumn: "Mined %", color: "#0080ff", yAxis: "right"} ] }; 

ここではすべおが簡単です


パラメヌタ䟡倀
parentSelectorチャヌトが再構築されるペヌゞの芁玠のID
幅600幅
高さ300身長
タむトル「鉄鉱山の仕事」芋出し
xColumn「日付」X軞の座暙が取埗される列の名前
xColumnDatetruetrueの堎合、x軞は日付です残念ながら、この機胜はただ未完成です。぀たり、x軞に日付しかプロットできたせん
yLeftAxisName「トン」巊のy軞の名前
yRightAxisName ""右のy軞の名前
カテゎリ私は長い間、それをどう呜名するかを考えたした。 これは「゜ケット」D3から飛び出し、カテゎリよりも優れたものはありたせんでした。 各カテゎリには、名前が蚭定されたす-デヌタでどのように衚蚘されるか、構造の幅
シリヌズ実際には、ダむアグラム自䜓、y軞の倀を取埗する列、色、およびダむアグラムが巊たたは右に属する軞を指定したす。

すべおの初期デヌタを蚭定し、最埌にプロットを呌び出しお結果を楜しみたす


  d3sChart(param,data,dataGroup); 

i04


このチャヌトには䜕が芋えたすか しかし、蚈画は過床に楜芳的であり、信頌性の高い予枬を行うためには、避けられない修正が必芁であるこずがわかりたす。 たた、制䜜を詳しく芋る必芁がありたす。実際のスケゞュヌルは痛々しいほど匕き裂かれおいたす...わかりたした、わかりたした-プログラマヌず呌ばれる人がいない堎所にすでに行きたした。


繰り返したすが、チャヌト䜜成関数の呌び出しを繰り返したす。


  d3sChart(param,data,dataGroup); 

それを芋るず、合理的な質問が生じたすが、おそらくあなたは私に尋ねたいでしょう-なぜ2぀のデヌタ配列、dataずdataGroupが関数に転送されたすか 私は答えたす軞のデヌタ範囲を正しく蚭定するには、初期デヌタ配列が必芁です。 これはあたりはっきりしおいないように思えたすが、すぐにこの点を説明しようず思いたす。


構築関数で最初に行うこずは、チャヌトを構築するオブゞェクトがあるかどうかを確認するこずです。 このオブゞェクト自䜓が存圚しない堎合、私たちは倚くのこずを誓いたす


 function d3sChart (param,data,dataGroup){ // check availability the object, where is displayed chart var selectedObj = null; if (param.parentSelector === null || param.parentSelector === undefined) { parentSelector = "body"; }; selectedObj = d3.select(param.parentSelector); if (selectedObj.empty()) { throw "The '" + param.parentSelector + "' selector did not match any elements. Please prefix with '#' to select by id or '.' to select by class"; }; 

次のアクションさたざたなむンデント、サむズを初期化し、スケヌルを䜜成したす。


  var margin = {top: 30, right: 40, bottom: 30, left: 50}, width = param.width - margin.left - margin.right, height = param.height - margin.top - margin.bottom; // set the scale for the transfer of real values var xScale = d3.time.scale().range([0, width]); var yScaleLeft = d3.scale.linear().range([height, 0]); var yScaleRight = d3.scale.linear().range([height, 0]); 

むンキュベヌションプロセスが人為的に加速されおいるため、ラむブラリがhatch化したばかりで、倖郚からの調敎むンデントなどに慣れおいないこずを忘れないでください。 したがっお、もう䞀床、私はあなたに理解しお蚱すようお願いしたす。


冗談ずしお冗談を蚀っおいたすが、䞊蚘のコヌドに戻りたす-むンデントずサむズを䜿甚するず、すべおが明確であるず思いたす。元の座暙を構築領域の座暙に倉換するためのスケヌルが必芁です。 x軞はタむムラむンずしお初期化され、巊右のy軞スケヌルは線圢ずしお初期化されおいるこずがわかりたす。 䞀般に、D3にはさたざたなスケヌルがありたすが、それらを考慮するこずは、他の倚くのこずず同様、この蚘事の範囲をはるかに超えおいたす。


継続しお、スケヌルを䜜成したした。次に、スケヌルを構成する必芁がありたす。 そしお、ここでその゜ヌスデヌタセットが圹立ちたす。 非垞に簡単な堎合-前のアクションで、グラフの座暙にスケヌルの範囲を蚭定し、次のコマンドでこの範囲をデヌタ範囲に関連付けたす。


  xScale.domain([d3.min(data, function(d) { return d[param.xColumn]; }), d3.max(data, function(d) { return d[param.xColumn]; })]); yScaleLeft.domain([0,d3.max(data, function(d) { return d[param.series[0].yColumn]; })]); yScaleRight.domain([0,d3.max(data, function(d) { return d[param.series[1].yColumn]; })]); 

Xスケヌルでは、デヌタの最小日付に最小倀を蚭定し、最倧倀に最倧倀を蚭定したす。 Y軞の堎合、最小倀ずしお0を䜿甚し、デヌタから最倧倀も孊習したす。 このため、最小倀ず最倧倀を芋぀けるために切れ目のないデヌタが必芁でした。


次のステップは、軞を蚭定するこずです。 ここから少し混乱が始たりたす。 D3にはスケヌルず軞がありたす。 スケヌルは、゜ヌス座暙をプロット領域の座暙に倉換する圹割を果たしたすが、軞は、チャヌト䞊に衚瀺される棒やダッシュをロシア語で衚瀺するように蚭蚈されおいたす。 したがっお、将来、スケヌルを曞く堎合、軞に぀いお話しおいるこずに泚意しおください。 チャヌトにスケヌルを描くこずに぀いお。


したがっお、Y軞甚に2぀のスケヌルずX軞甚に1぀のスケヌルがあるこずを思い出しおください。 問題の事実は、D3がデフォルトで日付スケヌルを衚瀺する方法に完党に䞍満だったこずです。 しかし、私が必芁ずする方法で日付の眲名を蚭定しようずするすべおの詊みは、このラむブラリの力ず蚘念碑の岩の䞊で波のように壊れたした。 したがっお、私は停造ず欺deに行かなければなりたせんでした私はX軞䞊に2぀のスケヌルを䜜成したした。 数か月間、出力から最初の月を陀倖する小さなチェックが远加されたした。 結局、ほんの数文前に、私はこのラむブラリを蚘念碑的なものだず非難したした。ここに、柔軟性の玠晎らしい䟋がありたす。


  var xAxis = d3.svg.axis().scale(xScale).orient("bottom") .ticks(d3.time.year,1).tickFormat(d3.time.format("%Y")) .tickSize(10); var monthNameFormat = d3.time.format("%m"); var xAxis2 = d3.svg.axis().scale(xScale).orient("bottom") .ticks(d3.time.month,2).tickFormat(function(d) { var a = monthNameFormat(d); if (a == "01") {a = ""}; return a;}) .tickSize(2); var yAxisLeft = d3.svg.axis().scale(yScaleLeft).orient("left"); var yAxisRight = d3.svg.axis().scale(yScaleRight).orient("right"); 

コヌドの怜蚎を続けたす。 すべおの準備䜜業を実斜し、珟圚、画像の圢成に盎接進んでいたす。 次の4行のコヌドは、 svg領域を順番に䜜成し、等高線フレヌムを描画し、指定されたオフセットを䜿甚しおsvgオブゞェクトのグルヌプを䜜成したす。この䞭にチャヌトが構築されたす。 最埌のアクション-タむトルが衚瀺されたす。


  var svg = selectedObj.append("svg") .attr({width: param.width, height: param.height}); // outer border svg.append("rect").attr({width: param.width, height: param.height}) .style({"fill": "none", "stroke": "#ccc"}); // create group in svg for generate graph var g = svg.append("g").attr({transform: "translate(" + margin.left + "," + margin.top + ")"}); // add title g.append("text").attr("x", margin.left) .attr("y", 0 - (margin.top / 2)) .attr("text-anchor", "middle").style("font-size", "14px") .text(param.title); 

次の倧きなコヌドは、3぀の軞の枬定単䜍に眲名したす。 すべおが明確であり、詳现に怜蚎する必芁はないず思いたす。


  g.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")") .call(xAxis) .append("text") .attr("x", width-20).attr("dx", ".71em") .attr("y", -4).style("text-anchor", "end") .text(param.xColumn); g.append("g").attr("class", "x axis2").attr("transform", "translate(0," + height + ")") .call(xAxis2); g.append("g").attr("class", "y axis") .call(yAxisLeft) .append("text").attr("transform", "rotate(-90)") .attr("y", 6).attr("dy", ".71em").style("text-anchor", "end") .text(param.yLeftAxisName); g.append("g").attr("class", "y axis").attr("transform", "translate(" + width + " ,0)") .call(yAxisRight) .append("text").attr("transform", "rotate(-90)") .attr("y", -14).attr("dy", ".71em").style("text-anchor", "end") .text(param.yRightAxisName); 

最埌に、チャヌト䜜成機胜の䞭心は、ダむアグラム自䜓の描画です。


  dataGroup.forEach(function(d, i) { for (var i = 0, len = param.categories.length; i < len; i += 1) { if (param.categories[i].name == d.key){ for (var j = 0, len1 = param.series.length; j < len1; j += 1) { if (param.series[j].yAxis == "left"){ // init line for left axis var line = d3.svg.line() .x(function(d) { return xScale(d[param.xColumn]); }) .y(function(d) { return yScaleLeft(d[param.series[j].yColumn] ); }); }; if (param.series[j].yAxis == "right"){ // init line for right axis var line = d3.svg.line() .x(function(d) { return xScale(d[param.xColumn]); }) .y(function(d) { return yScaleRight(d[param.series[j].yColumn] ); }); }; // draw line g.append("path").datum(d.values) .style({"fill": "none", "stroke": param.series[j].color, "stroke-width": param.categories[i].width}) .attr("d", line); }; }; }; }); 

「互いに埋め蟌たれた3぀のサむクル」-怒りで叫ぶ。 そしお、あなたはあなたのinりに絶察に正しいでしょう-私自身はそのような入れ子構造を䜜るのは奜きではありたせんが、時々そうしなければなりたせん。 ルヌプの3番目のネストでは、チャヌトラむンを初期化したす。シリヌズに応じお、このラむンが右目盛ず巊目盛のどちらに属するかを瀺したす。 その埌、2番目のネストで、グラフに線を衚瀺し、カテゎリプロパティから倪さを蚭定したす。 ぀たり 実際、構築に関䞎するコヌドは2行のみです。他のすべおは、チャヌト䞊の異なる数のダむアグラムを凊理するために必芁なバむンディングです。


さお、スケゞュヌルの最埌のアクションは、䌝説の結論です。 私は䌝説を悔い改めたした-私はすでに急いでいお倱敗したした、このコヌドはすぐに曞き換えられ、D3ではすべおが非垞にシンプルであるこずをもう䞀床実蚌するためだけにそれを瀺したす。 そしお、ただ-あなたがする必芁がない方法の良い䟋です


 var legend = svg.append("g").attr("class", "legend").attr("height", 40).attr("width", 200) .attr("transform", "translate(180,20)"); legend.selectAll('rect').data(param.series).enter() .append("rect").attr("y", 0 - (margin.top / 2)).attr("x", function(d, i){ return i * 90;}) .attr("width", 10).attr("height", 10) .style("fill", function(d) {return d.color; }); legend.selectAll('text').data(param.series).enter() .append("text").attr("y", 0 - (margin.top / 2)+10).attr("x", function(d, i){ return i * 90 + 11;}) .text(function(d) { return d.yColumn; }); // add legend for categories var legend1 = svg.append("g").attr("class", "legend").attr("height", 40).attr("width", 200) .attr("transform", "translate(350,20)"); legend1.selectAll('line').data(param.categories).enter() .append("line").attr("y1", 0 - (margin.top / 2)+5).attr("x1", function(d, i){ return i * 60;}) .attr("y2", 0 - (margin.top / 2)+5).attr("x2", function(d, i){ return i * 60+15;}) .style("stroke", "black").style("stroke-width", function(d) { return d.width; }); legend1.selectAll('text').data(param.categories).enter() .append("text").attr("y", 0 - (margin.top / 2)+10).attr("x", function(d, i){ return i * 60 + 17;}) .text(function(d) { return d.name; }); 

以䞊です。 ご枅聎ありがずうございたした 私の蚘事であなたを倱望させなかったこずを願っおいたす。


コヌドず゜ヌスデヌタのサンプルは、 Githubからダりンロヌドできたす。


結論ずしお、D3ラむブラリを自分で理解しようずしたずきに、同様の蚘事たたはチュヌトリアルを探しおいたこずを付け加えたす。 デヌタをロヌドおよび準備する方法、構築゚リアを䜜成および構成する方法、このデヌタを衚瀺する方法など、䟋を䜿甚しお個別に順番に衚瀺される蚘事を探しおいたした。 残念ながら、そのようなものは芋られたせんでした。著者のD3の䟋ではすべおが混同されおいるため、䜜業のロゞックを理解せず、このラむブラリの最初の知識もありたせん。このデヌタの衚瀺、およびその逆。


2016幎6月23日曎新 Githubでプログラムを曎新したした。タむムラむンを完成させ、X軞の日付の代わりに数倀シリヌズを開始できるようにし、いく぀かの゚ラヌを修正し、さらにカテゎリなしでグラフを䜜成できるようになりたした。


2016幎8月12日曎新 d3の4番目のバヌゞョンで動䜜するようにプログラムを再線集したした。 倚くの非互換性が出たした。 この蚘事で説明する䟋は、ラむブラリの3番目のバヌゞョンでのみ機胜し、ファむル名に_v3プレフィックスが付いたファむルのgithubにありたす。



Source: https://habr.com/ru/post/J303834/


All Articles