7

I used VictoryChart to implement a pie graph and it works fine...

render() {

    const data_distribution = this.state.pie_keys.map((d) => ({x:d, y:this.state.pie_data[d], label:d }));

    return (
      <div className="App">
        <div className="pie_wrapper">
          <VictoryPie
            labelComponent={<VictoryTooltip cornerRadius={0} />}   
            padAngle={0.5}
            innerRadius={100}
            width={400} height={400}
            style={{ 
              labels: { fontSize: 15, fill: "black"},
              data: {
                  fillOpacity: 0.9, stroke: "white", strokeWidth: 3
              }
            }}
            labelRadius={90}
            data = {data_distribution}
          />
        </div>
      </div>
    );
  }

It is important to note that data_distribution simply looks as follows...

data={[
    { x: "Fac of Engineering & Appl Sci", y: 8.557902403495994 },
    { x: "Faculty of Arts and Science", y: 53.775188152464196 },
    { x: "Faculty of Education", y: 13.085700412721534 },
    ...
    ...
  ]}

So I'm wondering if I can set the color for each piece of the pie within the data object. The documentation specifies that...

color scales: "grayscale", "qualitative", "heatmap", "warm", "cool", "red", "green", "blue". VictoryPie will assign a color to each slice by index, unless they are explicitly specified in the data object.

This says that you can but I cannot figure out how. I tried the following...

data={[
    { x: "Fac of Engineering & Appl Sci", y: 8.557902403495994, colorScale:"red" },
    { x: "Faculty of Arts and Science", y: 53.775188152464196, colorScale:"red" },
    { x: "Faculty of Education", y: 13.085700412721534, colorScale:"red" },
    ...
    ...
]}

which translates to...

const data_distribution = this.state.pie_keys.map((d) => ({x:d, y:this.state.pie_data[d], label:d, colorScale:"red"}));

However, they do not turn red. I cannot find an example online. Is this possible to do or not?

I know that I can define colorScale: {[...]} but that is not what I'm asking. I want to be able to set each piece of the pie from within the data object.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
buydadip
  • 8,890
  • 22
  • 79
  • 154
  • The first line inside your render function, which looks like your `data_distribution` mapping, doesn't include `colorScale` at all ... is this intentional ? – Alex McMillan Aug 01 '18 at 05:47
  • @AlexMcMillan I do not know how to include it, that is essentially what I am trying to figure out. – buydadip Aug 01 '18 at 05:49
  • @AlexMcMillan I tried the following `const data_distribution = this.state.pie_keys.map((d) => ({x:d, y:this.state.pie_data[d], label:d, colorScale:"red"}));` because I do not know how to do it properly – buydadip Aug 01 '18 at 05:50
  • try putting a `console.log(data_dictionary);` immediately after that line, and look in the browser console at what you're creating. See if it makes sense to you and is what you expected, then compare it to the documentation for the `VictoryChart` library – Alex McMillan Aug 01 '18 at 05:54
  • Okay, it looks like you need to pass `colorScale: ["red"]` as an argument to the `VictoryPie` component, not as a field in your data. Try `` – Alex McMillan Aug 01 '18 at 05:55
  • @AlexMcMillan Yeah I can do this, but `VictoryChart` documentation states that I can also set it explicitly within the data object and I'm trying to find out how and if it is possible. – buydadip Aug 01 '18 at 05:58

6 Answers6

9

I know this question has been "answered", but the answer is unsatisfactory to me. Since I came here and left frustrated, I've decided to post my solution in the hopes that others might get some use out of it.

You can override the colors for individual slices using the style prop:

<VictoryPie
  data={[
    { x: "Cats", y: 35 },
    { x: "Dogs", y: 40 },
    { x: "Birds", y: 55 }
  ]}
  style={{
    data: {
      fill: ({ y }) =>
        y > 49 ? 'green'
        : y > 39 ? 'yellow'
        : 'tomato'
    }
  }}
/>

In this case, you should not use colorScale at all. I've read the source, and far as I can tell, this is the only way to do a data-based color mapping in VictoryPie.

Jon Hieb
  • 760
  • 8
  • 9
  • Thank you, I was frustrated as well and chose an answer that was unsatisfactory. – buydadip Oct 31 '18 at 19:51
  • For some reason, this does tomato for all data – wobsoriano Jun 19 '20 at 12:38
  • On the fill callback you get the current item. You have access its index and to the original data. That way you can do the following: fill: d => d.data[d.index].colorScale – Inbar Barkai Jun 23 '20 at 07:48
  • The latest version of victory passes a different value to the callback. You can access `y` via `obj.slice.data.y`. – Justin Johnson Nov 24 '20 at 01:23
  • FYI: Using `datum.y` instead of `y` should solve the problem: ```data: { fill: ({ datum }) => datum.y > 49 ? 'green' : datum.y > 39 ? 'yellow' : 'tomato' }``` – Nam Vu Jun 29 '21 at 04:58
7

You can derive the fill color based on your data, but you have to provide that logic. Your accepted answer applies color based on y value, however you can also specify the color in the data itself so long as you set the style to do so:

<VictoryPie
  data={[
    { x: "Cats", y: 35, yourAttribute: "#0000FF" },
    { x: "Dogs", y: 40, yourAttribute: "#00FF00" },
    { x: "Birds", y: 55, yourAttribute:"#FF0000" }
  ]}
  style={{
    data: {
      fill: (d) => d.yourAttribute
    }
  }}
/>
WOLVERTRON
  • 131
  • 2
  • 2
5

For the current version

"victory": "^35.4.0",
"victory-native": "^35.3.1"

you can do:

 <VictoryPie
        data={[
            { x: "Cats", y: 35, fill: "gold" },
            { x: "Dogs", y: 40, fill: "tomato" },
            { x: "Birds", y: 55, fill: "navy" }
        ]}
        style={{
            data: {
                fill: ({datum}) => datum.fill
            }
        }}
   />

See https://formidable.com/open-source/victory/docs/common-props/#data

pors
  • 3,856
  • 36
  • 33
3

I manually added a function to generate randomly try if this helps you `var stats = this.state.statistics;

var stat = this.state.statistic;
        if (stats.length>0) {
            if(this.colors.length<stats.length){
                for(let i=0;i<stats.length;i++){
                    this.colors.push(getRandomColor());
                }
            }
        }
        const color = ['#9167f1', '#f26893', '#99cfef', '#99ef9a'];
        const colors = this.colors;`

function getRandomColor() {
    var letters = '0123456789abcdef';
    var color = '#';
    for (var i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
    }
    return color;
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
pageNotfoUnd
  • 666
  • 10
  • 20
2

If you know the data rendering order then you can use the colors in same order it will automatically take the color in order

<VictoryPie
  colorScale={["black", "red", "green", "#4cc9ff", "navy" ]}
  data={sampleData}
/>
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
pageNotfoUnd
  • 666
  • 10
  • 20
  • That is true and I have noticed that, but if you have more pieces then colors, then it will start choosing colors at random after you have exhausted all the colors. – buydadip Aug 01 '18 at 05:55
  • Essentially I am trying to figure out a way in which I can keep track of what color is being used by each piece if I have more pieces then colors. – buydadip Aug 01 '18 at 05:57
  • Seeing as I couldn't figure out what I wanted, this was the approach I ended up going with. – buydadip Aug 04 '18 at 06:04
2

I think instead of colorScale: "red", you want fill: "red" in the data object since Victory's Slice primitives are just svg <Path> elements.