Contents

Let’s visualize a bar graph in blender

Data visualization is an integral part of anything to do with IT.  Be it when dealing Database monitoring, Observability metrics or Revenue streams or anything else.

Let’s begin

In this article we’ll explore how we can automate the visualization of a large air quality index dataset using open source tools.

(Note  that we are not building any dashboards here, just an animated bar graph that you can show off in presentations or your website.)

What we need

  • Any blender version upwards of 2.8. In this project I am using 4.5.5. 

  • Python 3.9 

  • Libreoffice calc / MS excel 

  • Freely available dataset from Kaggle 

 

Downloading the dataset

For this project, we will be using this dataset from Kaggle. The idea is to visualize the level of O3 in the environment in different cities over time.

You could also go a step further and calculate the AQI for each city.

Prepping the data

Open up the csv in any spreadsheet software of your choice (Calc for me).

First we sort the data in increasing order of time.
Then we can optionally fill in missing values in the columns we want; or we can handle this in the visualization part by ignoring those entries altogether or averaging the values.

To make things easier, I have also created a separate file with the names of all the cities separated by commas.

Scripting the graph

The purpose of our python scripts is to automate this graph creation and animation process in blender.

Creating the graph

The initial scene will contain a sample bar and sample text along with the axes. We will tell blender to read from our “cities list file” and create the bars and text for all cities.

Initial scene

 

Below is the code for generating the bars in the graph:-

create-graph.py

 

import random

# Define the path to the file
file_path = './air-quality-data-in-india/all_cities.txt'

city_list = []

# Read the file and split contents by comma
try:
    with open(file_path, 'r') as file:
        content = file.read()
        city_list = [city.strip() for city in content.split(',') if city.strip()]
        print("Cities loaded:", city_list)
except FileNotFoundError:
    print(f"File not found at path: {file_path}")
except Exception as e:
    print(f"An error occurred: {e}")

import bpy

scene = bpy.context.scene

# Get the reference strips
bar_sample = bpy.data.objects["Plane"]
text_sample = bpy.data.objects["Text"]

# Strip duration and frame position
duration = 720
start_frame = 0
frame_gap = 0  # Optional spacing

displacement = 3

for idx, city in enumerate(city_list):
    # Step 2: Compute frame start
    frame_start = start_frame + idx * frame_gap

    # Duplicate the bar with linked data (same mesh)
    duplicate = bar_sample.copy()
    duplicate.data = bar_sample.data  # Link the same mesh datablock
    duplicate.animation_data_clear()  # Optional: clear animation data

    # Optionally offset the duplicate to avoid overlap
    duplicate.location.x += displacement * (idx + 1)

    # Link the new object to the same collection(s) as the original
    for collection in bar_sample.users_collection:
        collection.objects.link(duplicate)
        
    duplicate.name = f"{city}_bar"
    
    # Duplicate the text
    duplicate_text = text_sample.copy()
    duplicate_text.data = text_sample.data.copy()  # Duplicate mesh data (new mesh datablock)
    duplicate_text.animation_data_clear()       # Optional

    # Offset to see the duplicate
    duplicate_text.location.x += displacement * (idx + 1)

    # Link the duplicate to the same collection(s)
    for collection in text_sample.users_collection:
        collection.objects.link(duplicate_text)

    duplicate_text.data.body = city

 

Bars generated

 


Animating the graph

Once the scene is created, we can now tell blender to animate the graph based on the values in the cleaned up csv file:-

animate-graph.py

 

import csv
import bpy


def get_o3_naqi_category(o3_value):
    """
    Takes O₃ concentration in µg/m³ (8-hour average) and returns a category number:
    1 = Good, 2 = Satisfactory, 3 = Moderate, 4 = Poor, 5 = Very Poor, 6 = Severe
    """
    if o3_value <= 50:
        return 1  # Good
    elif o3_value <= 100:
        return 2  # Satisfactory
    elif o3_value <= 168:
        return 3  # Moderate
    elif o3_value <= 208:
        return 4  # Poor
    elif o3_value <= 748:
        return 5  # Very Poor
    else:
        return 6  # Severe


cur_frame = 0

# 109.26 max in camera (amravati as reference)
max_in_camera_value_scale = 0.276 

#(0:0.65) <=> 0:257.73 #max value
max_value = 257.73 
min_value =  0.65

length_ref = bpy.data.objects["length_reference"]

with open('./air-quality-data-in-india/city_day (cleaned-up).csv', newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        city = row['City']
        value = row['O3']

        bar = bpy.data.objects[city+'_bar']        
        flag = 0
        try:    
            float(value)
        except:
            flag = 1
            pass
        
        if(flag==0):
            value = float(value)
                
            bar.scale[1] = value * min_value / max_value
            
            if (bar.scale[1] >= max_in_camera_value_scale):
                delta_perc = 1 - max_in_camera_value_scale - bar.scale[1]
            else:
                delta_perc = 1
            length_ref.scale[1]=delta_perc
            length_ref.keyframe_insert(data_path='scale',frame=cur_frame)
            
            
            
            bar.keyframe_insert(data_path='scale', frame=cur_frame)
            
            color_start = (0.0, 0.0, get_o3_naqi_category(value)/6, 0.0)  # Red (RGBA)
            bar.color = color_start
            bar.keyframe_insert(data_path="color", frame=cur_frame)

        
        cur_frame += 2

 


Once the keyframes have been set, you can preview the animation in the 3d view.

Graph with keyframes generated

 

As a bonus, using the timeline, you can also clean up the keyframes or even better,
add / duplicate a set of keyframes to fill in the gaps to simulate data cleanup.

 

Rendering the graph

Its time to add colours to the bars and render the video. Use emission shaders (pure light, no shadows) with clever driver magic to animate the color of the bars. That, and a complementary background color for the sky adds to the perfection. 

 

Your graph should look something like this