Rechartsを使用したグラフ実装例と小技
株式会社 Rehab for JAPAN エンジニアのもじゃ(@moja_moja)です。
私がこのブログを書いている段階では、新規プロダクトでフロントエンドエンジニアをしており、その中で Recharts を使用したグラフをいくつか実装しました。
Rehab for JAPAN は複数のプロダクトが存在しますが、私が関わるプロダクト以外では Recharts を導入していません。
今回は他プロダクトのメンバーや社外でグラフの実装を検討している人に向けて、少しでも参考になるような、グラフの実装例やちょっとした小技・実装時に困った点・解決策の一部を紹介していきたいと思います。
なお、紹介するサンプルコードは公式の Examples ページから実行することもできます。
文末の「おまけ サンプルコードの実行方法」に手順を記載しますので、こちらを実行しながら見ていただくとより理解していただけると思います。
Recharts とは?
React でグラフを簡単に描画できるライブラリで折れ線・棒・円・レーダー・放射棒・散布図・エリアチャートなどの様々なグラフを描画することができます。
また、折れ線グラフ + 棒グラフのような 表示領域に複数のグラフを描画することもできます。
今回は折れ線・棒・円・折れ線 + 棒グラフのサンプルコードを紹介していきたいと思います。
折れ線グラフの実装例
折れ線グラフのサンプルコードと使用するコンポーネントについて紹介します。
import React, { PureComponent } from "react";
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Legend,
ResponsiveContainer,
} from "recharts";
const chartData = [
{
name: "ハンバーガー",
score: 3,
},
{
name: "ポテト",
score: 4.5,
},
{
name: "ドリンク",
score: 1.5,
},
{
name: "サイドメニュー",
score: 5,
},
];
export default class Example extends PureComponent {
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<LineChart
width={500}
height={300}
data={chartData}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis domain={[0, 5]} />
<Legend />
<Line dataKey="score" stroke="#82ca9d" />
</LineChart>
</ResponsiveContainer>
);
}
}
まず、chart で表示したデータはサンプルコードのchartData
のように配列で定義する必要があります。
chartData
は<LineChart/>
コンポーネントに<LineChart data={chartData}/>
のように定義をします。
ただ、これだけでは 折れ線を表示することはできません。
次に X 軸に名称を表示し、折れ線の dot も表示させていきたいと思います。
名称と dot の表示方法
各要素に対して、dataKey="~"
を記述することでデータを反映させることができます。
今回の場合、X 軸には chartData
内のname
を記述し、折れ線の dot はscore
と記述することで表示することができます。
<XAxis dataKey="name" />
<Line dataKey="score" stroke="#82ca9d" />
domain の設定方法
<YAxis/>
コンポーネントに domain を記述することで Y 軸の最小値と最大値を設定することができます。
// デフォルト
<YAxis domain={[0, "auto"]} />
// chartData内の最小値と最大値を設定
<YAxis domain={['dataMin', 'dataMax']} />
// chartData内の最小値と最大値に加減することも可能
<YAxis domain={['dataMin - 10', 'dataMax + 10']} />
domain は数字だけでなくdataMax
のような文字列にも対応しています。
domain={['dataMin', 'dataMax']}
と定義すると chartData の score を参照し、score の最大・最小値が Y 軸の数字として表示されます。
今回の例だと、dataMin は 1.5 で dataMax が 5 になり、Y 軸には 1.5 と 5 が表示されるようになります。
また、domain={['dataMin - 10', 'dataMax + 10']}
と定義すると最大・最小値から加減した数字を表示することが可能です。
今回の例だと、Y 軸は最小値は-8.5・最大値は 15 と表示されます。
使用されているコンポーネントの役割
簡単ではありますがサンプルコードで実装しているコンポーネントを紹介したいと思います。
-
<LineChart />
折れ線グラフコンポーネント -
<Line />
データに紐づいた折れ線コンポーネント -
<XAxis/>
X 軸を表示させるコンポーネント -
<YAxis/>
Y 軸を表示させるコンポーネント -
<Legend>
凡例を表示させるコンポーネント -
<CartesianGrid/>
グラフにグリッドを表示させるコンポーネント -
<ResponsiveContainer/>
チャートを親要素のサイズに適応させるコンポーネント
棒グラフの実装例
次に棒グラフのサンプルコードを紹介します。
import React, { PureComponent } from "react";
import {
BarChart,
Bar,
XAxis,
YAxis,
LabelList,
CartesianGrid,
Legend,
ResponsiveContainer,
} from "recharts";
const chartData = [
{
name: "ハンバーガー",
sales: 10000,
},
{
name: "ポテト",
sales: 20000,
},
{
name: "ドリンク",
sales: 5000,
},
{
name: "サイドメニュー",
sales: 30000,
},
];
export default class Example extends PureComponent {
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<BarChart
width={500}
height={300}
data={chartData}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis domain={[0, "dataMax + 5000"]} />
<Legend />
<Bar dataKey="sales" fill="#8884d8">
<LabelList fill="#000000" />
</Bar>
</BarChart>
</ResponsiveContainer>
);
}
}
最初に紹介した折れ線グラフとほとんど同じような構成で棒グラフを実装することが可能です。
<YAxis />
の domain の記述方法ですが今回の場合、chartData
の sales
の最大値である30000
に5000
を加算とした 35000
が上限として Y 軸に表示されます。
LabelList の役割
新しく<LabelList/>
コンポーネントを追加しました。
<LabelList/>
を追加することで、dataKey="sales"
に紐づいたデータを 棒グラフ内に表示することができます。
なお、<Label>
コンポーネントも存在しますが、こちらは<XAxis>
や<YAxis>
にdataKey
と別で名称をつけたい時に使用します。
<XAxis dataKey="name">
<Label value="ハンバーガー売り上げ高" offset={0} position="insideBottom" />
</XAxis>
円グラフ
import React, { PureComponent } from "react";
import { PieChart, Pie, ResponsiveContainer } from "recharts";
const PieData = [
{ name: "とても満足", rate: 6.5 },
{ name: "満足", rate: 57.2 },
{ name: "普通", rate: 20.9 },
{ name: "不満", rate: 9.6 },
{ name: "とても不満", rate: 5.8 },
];
export default class Example extends PureComponent {
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<PieChart width={500} height={500}>
<Pie
data={PieData}
dataKey="rate"
cx="50%"
cy="50%"
outerRadius={100}
fill="#8884d8"
/>
</PieChart>
</ResponsiveContainer>
);
}
}
円グラフはこれまで紹介した折れ線や棒グラフとは少し異なる形で実装します。
サンプルコードのような、<PieChart/>
と<Pie />
コンポーネントのみで、円グラフは描画できます。
しかし、描画している円グラフはいくつか課題があります。
今回のサンプルコードの場合、アンケート結果を円グラフで表示していますが、各スライスが同じ色である点や、rate
の数字がでていないためわかりづらくなっています。
次に色を分けて表示する方法と数字の表示方法について紹介します。
色を分けて表示する方法
簡単なやり方としては、const COLORS
のようにカラーコードを配列で定義します。
そして、<Pie/>
コンポーネント内に<Cell />
コンポーネントを 記述し、map 処理させることでそれぞれ異なる色で表示させることが可能になります。
import React, { PureComponent } from "react";
import { PieChart, Pie, Sector, Cell, ResponsiveContainer } from "recharts";
const PieData = [
{ name: "とても満足", rate: 6.5 },
{ name: "満足", rate: 57.2 },
{ name: "普通", rate: 20.9 },
{ name: "不満", rate: 9.6 },
{ name: "とても不満", rate: 5.8 },
];
const COLORS = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042"];
export default class Example extends PureComponent {
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<PieChart width={500} height={500}>
<Pie
data={PieData}
dataKey="rate"
cx="50%"
cy="50%"
outerRadius={100}
fill="#8884d8"
>
{PieData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
/>
))}
</Pie>
</PieChart>
</ResponsiveContainer>
);
}
}
数字 を表示させる方法
各 スライスに対して数字を表示させたい場合は<Pie/>
コンポーネントにlabel
を追加することでdataKey="rate"
の数字を表示させることができます。
<Pie
data={PieData}
dataKey="rate"
+ label // 追加
>
他のケースとして、棒グラフの LabelList
のように スライス の中に数字を表示したいというケースもあると思います。
その場合は label
に関数を定義することで実現ができます。
const RADIAN = Math.PI / 180;
const customizedLabel = ({
cx,
cy,
midAngle,
innerRadius,
outerRadius,
percent,
}) => {
const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
const x = cx + radius * Math.cos(-midAngle * RADIAN);
const y = cy + radius * Math.sin(-midAngle * RADIAN);
return (
<text
x={x}
y={y}
fill="white"
textAnchor={x > cx ? "start" : "middle"}
dominantBaseline="central"
fontSize={16}
>
{`${(percent * 100).toFixed(0)}%`}
</text>
);
};
export default class Example extends PureComponent {
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<PieChart width={500} height={500}>
<Pie
data={PieData}
dataKey="rate"
cx="50%"
cy="50%"
outerRadius={100}
fill="#8884d8"
label={customizedLabel}
labelLine={false}
isAnimationActive={false}
>
{PieData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
/>
))}
</Pie>
</PieChart>
</ResponsiveContainer>
);
}
}
サンプルコードのように実装することで各スライス内に数字を表示させることができます。
また、label
に関数を定義する方法は LabelList
コンポーネント でも同様のことができます。
注意点として、表示している数字は SVG 要素の<text>を使用しているので CSS のmargin
、padding
、position
などは私が試した限りでは反映できませんでした。
折れ線 + 棒グラフの混合グラフの実装例
ここまでは基本的な実装例を紹介をしてきましたが、次からは応用編になります。
import React, { PureComponent } from "react";
import {
ComposedChart,
Bar,
Line,
LabelList,
XAxis,
YAxis,
CartesianGrid,
Legend,
ResponsiveContainer,
} from "recharts";
const chartData = [
{
name: "Page A",
pv: 2400,
rate: 19.6,
},
{
name: "Page B",
pv: 1398,
rate: 13.0,
},
{
name: "Page C",
pv: 9800,
rate: 12.5,
},
{
name: "Page D",
pv: 3908,
rate: 14.0,
},
{
name: "Page E",
pv: 4800,
rate: 11.0,
},
{
name: "Page F",
pv: 3800,
rate: 19.3,
},
{
name: "Page G",
pv: 4300,
rate: 11.5,
},
];
export default class Example extends PureComponent {
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
width={500}
height={300}
data={chartData}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis yAxisId="bar" />
<YAxis domain={[0, 20]} yAxisId="line" orientation="right" />
<Legend />
<Bar dataKey="pv" fill="#82ca9d" yAxisId="bar">
<LabelList />
</Bar>
<Line dataKey="rate" yAxisId="line" fill="#83a6ed">
<LabelList position="top" />
</Line>
</ComposedChart>
</ResponsiveContainer>
);
}
}
折れ線と棒グラフを同時表示させるためには<ComposedChart/>
の使用と<YAxis/>
を追加します。
ポイントとしては<YAxis/>
と<Bar/>
と<Line/>
にそれぞれyAxisId="~"
を追加することです。
どちらの<YAxis/>
にもyAxisId="~"
記述されていない場合、画像のように折れ線がうまく表示できなくなります。
どちらか片方の<YAxis/>
にyAxisId="~"
を記述することでも問題なく表示はできますが、さらに折れ線や棒グラフを追加するケースもあると思います。
そのため、個人的には他の人が見た時にyAxisId="~"
が記述されている・されていないものの違いに混乱しないように、<YAxis yAxisId="~"/>
と定義した方が良いと考えています。
<YAxis yAxisId="bar"/> // <Bar yAxisId="bar"/>と紐づいている
<YAxis
domain={[0, 20]}
yAxisId="line" // <Line yAxisId="line"/>と紐づいている
orientation="right"
allowDataOverflow
/>
<Legend />
<Bar dataKey="pv" fill="#82ca9d" yAxisId="bar">
<LabelList />
</Bar>
<Line dataKey="rate" yAxisId="line" fill="#83a6ed">
<LabelList position="top" />
</Line>
Y 軸の表示位置について
折れ線 + 棒グラフの混合グラフを確認すると、比率の値が Y 軸の右側に表示しています。
これは<YAxis orientation="right"/>
と記述しているからです。
しかし、用途によっては比率を Y 軸に表示しないケースもあると思います。
その場合は<YAxis hide/>
と記述することで Y 軸に比率が表示されなくなります。
<YAxis
domain={[0, 20]}
yAxisId="line"
- orientation="right"
+ hide
/>
domain の設定について
<YAxis/>
でdomain={[0, 20]}
で設定しているが、chartData 内で参照する値が、domain で設定している数字よりも大きい値がある場合、通常はライブラリ側で Y 軸の値を自動で dataMax の値になって表示されます。
const chartData = [
{
name: "Page A",
pv: 2400,
+ rate: 29.6
- rate: 19.6
},
~
];
<YAxis domain={[0, 20]} yAxisId="line" orientation="right" />
本来、domain={[0, 20]}
と設定しているため、Y 軸の最大値は 20 と表示されるはずが画像のように 29.6 が最大値として Y 軸に表示されます。
場合によっては Y 軸の値は domain の最大値のままで表示しておきたいケースもあると思います。
その場合は<YAxis/>
に allowDataOverflow
を追加すると domain は設定した最大値のまま Y 軸に表示され、最大値以上の値はオーバーフローして表示されるようになります。
<YAxis
domain={[0, 20]}
yAxisId="line"
orientation="right"
+ allowDataOverflow
/>
これによって、 domain で設定した最大値以上の数字があった場合、画像のように、表示領域から折れ線の dot がオーバーフローした形で表示され、Y 軸の最大値は domain で設定した値が表示されるようになります。
実装時に困った点
関わるプロダクトで折れ線グラフの上部に数字を小数点第一位まで表示したいとの要望がありました。
問題として、先述で記載したコードのように domain={[0, 20]}
のように指定した状態で、chartData の rate が19.6
のように最大値に近い小数点の数字で、かつ<LabelList />
コンポーネントに position="top"
を記述すると上限に近い数字が見切れてしまう問題がありました。
サンプルコードの場合は、rate: 19.6
の部分が見切れているのを確認できます。
また、上限に近いものは position="top"
から"bottom”
に変えて欲しいとの要望があり、実装する私としてはかなり困ってしまいました。
次に考えた策と実際にどのように実装したを紹介したいと思います。
解決策
-
domain={[0, "dataMax + 5"]}
のような記述にする
dataMax
は先述しているように加算をすることができるため、見切れない程度に値を加算してあげることで解決できるのではと考えました。
しかし、Y 軸の値を表示させたい場合は Y 軸で表示される上限値は"dataMax + 5"
になります。
そのため、Y 軸の上限値はデータによって変動する問題があり、今回の場合だと domain で設定した値で表示したいため、不採用となりました。
Y 軸を表示させないケースの場合は<YAxis domain={[0, "dataMax + 5"]} hide/>
のように 記述すると解消します。
-
<LabelList/>
の content にカスタム Label を作成して対応した
円グラフのスライス内で数字を表示させる時のように、LabelList
に content
を記述すると関数を定義することができます。
const CustomLabel = (props) => {
const { x, y, value } = props;
const yPos = value > 19 ? y + 20 : y - 10;
return (
<text x={x} y={yPos} fontSize={16} textAnchor="middle">
{value}
</text>
);
};
<Line dataKey="rate" yAxisId="line" fill="#83a6ed" isAnimationActive={false}>
<LabelList content={<CustomLabel />} />
</Line>;
props
では x 座標・Y 座標・dataKey に紐づく値(value)を取得できます。
yPos
には value の値 が 19 を条件として、 y 座標の値を加算 or 減算をする処理を記述し、text 要素の y に入れることで、上限に近い数字は折れ線グラフの dot よりも下に表示されるようになります。
この方法を使用するとdomain={[0, "dataMax + 5"]}
の記述のように、Y 軸で表示する値が変動せずに上限に近い数字は下に表示されるため、domain は記述したもので表示されるようになります。
完成したサンプルコードは下記になります。
import React, { PureComponent } from "react";
import {
ComposedChart,
Bar,
Line,
LabelList,
XAxis,
YAxis,
CartesianGrid,
Legend,
ResponsiveContainer,
} from "recharts";
const data = [
{
name: "Page A",
pv: 2400,
rate: 19.6,
},
{
name: "Page B",
pv: 1398,
rate: 13.0,
},
{
name: "Page C",
pv: 9800,
rate: 12.5,
},
{
name: "Page D",
pv: 3908,
rate: 14.0,
},
{
name: "Page E",
pv: 4800,
rate: 11.0,
},
{
name: "Page F",
pv: 3800,
rate: 19.3,
},
{
name: "Page G",
pv: 4300,
rate: 11.5,
},
];
const CustomLabel = (props) => {
const { x, y, value } = props;
const yPos = value > 19 ? y + 20 : y - 10;
return (
<text x={x} y={yPos} fontSize={16} textAnchor="middle">
{value}
</text>
);
};
export default class Example extends PureComponent {
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
width={500}
height={300}
data={data}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis yAxisId="bar" />
<YAxis domain={[0, 20]} yAxisId="line" orientation="right" />
<Legend />
<Bar dataKey="pv" fill="#82ca9d" yAxisId="bar">
<LabelList />
</Bar>
<Line
dataKey="rate"
yAxisId="line"
fill="#83a6ed"
isAnimationActive={false}
>
<LabelList content={<CustomLabel />} />
</Line>
</ComposedChart>
</ResponsiveContainer>
);
}
}
終わりに
今回は Recharts を使用した折れ線グラフ・棒グラフ・円グラフ・折れ線 + 棒グラフの実装例や小技・困った点と解決策を紹介しました。
紹介した実装例以外にも実装したグラフや小技・困った点・解決策はありますが、ボリュームが多くなってきたので今回はここまでにします。
紹介できなかったグラフの実装例はまた別の機会に紹介します。
おまけ サンプルコードの実行方法
紹介したサンプルコードをコピーした後、Examples に遷移します。
その後、下記の添付している画像の赤枠にコピーしたコードを貼り付けて、「Run」を実行すると紹介したコードのチャートが表示されると思います。
Discussion