Plotly animated bubble chart: no data in the plot
Asked Answered
W

1

0

I am trying to adapt the standard plotly animated bubble chart to a csv file with these columns:

index,country,year,Country code,Total population (Gapminder),Life satisfaction in Cantril Ladder (World Happiness Report 2017),GDP per capita
62,Afghanistan,2008,AFG,29839994.0,3.723589897,1298.14315888
63,Afghanistan,2009,AFG,30577756.0,4.401778221,1531.17399272
64,Afghanistan,2010,AFG,31411743.0,4.75838089,1614.25500126
65,Afghanistan,2011,AFG,32358260.0,3.83171916,1660.73985618
66,Afghanistan,2012,AFG,33397058.0,3.782937527,1839.27357928
67,Afghanistan,2013,AFG,34499915.0,3.572100401,1814.15582533
167,Albania,2007,ALB,3169665.0,4.634251595,8447.88228539
169,Albania,2009,ALB,3192723.0,5.485469818,9524.60981095
170,Albania,2010,ALB,3204284.0,5.268936634,9927.13514733
171,Albania,2011,ALB,3215988.0,5.867421627,10207.7006745
172,Albania,2012,ALB,3227373.0,5.510124207,10369.7616592
173,Albania,2013,ALB,3238316.0,4.550647736,10504.0930888
242,Algeria,2010,DZA,35468208.0,5.46356678,12870.2162376
243,Algeria,2011,DZA,35980193.0,5.317194462,12989.9549601
244,Algeria,2012,DZA,36485828.0,5.604595661,13161.566464
451,Angola,2011,AGO,19618432.0,5.589000702,5911.25433387
452,Angola,2012,AGO,20162517.0,4.360249996,5998.63860099
453,Angola,2013,AGO,20714494.0,3.937106848,6185.0138292

The size of the datapoints will be a function of the population, and I'll plot life satisfaction as a function of the country's gdp. I work a bit on the dataset:

gdp=pd.read_csv('gdp-vs-happiness.csv')

gdp=gdp.ix[~(gdp['year'] < 2005)]
gdp=gdp.dropna()

dataset = gdp

and then here is the code:

years = ['2005','2006', '2007','2008','2009','2010','2011','2012','2013','2014','2015','2016']

# make list of continents
countries = []
for country in dataset['country']:
    countries.append(country)

# make figure
figure = {
    'data': [],
    'layout': {},
    'frames': []
}
config = {'scrollzoom': True}

# fill in most of layout
figure['layout']['xaxis'] = {'title': 'GDP per Capita', 'type': 'log'}
figure['layout']['yaxis'] = {'range': [0, 10], 'title': 'Life Satisfaction'}
figure['layout']['hovermode'] = 'closest'
figure['layout']['sliders'] = {
    'args': [
        'slider.value', {
            'duration': 400,
            'ease': 'cubic-in-out'
        }
    ],
    'initialValue': '2005',
    'plotlycommand': 'animate',
    'values': years,
    'visible': True
}
figure['layout']['updatemenus'] = [
    {
        'buttons': [
            {
                'args': [None, {'frame': {'duration': 500, 'redraw': False},
                         'fromcurrent': True, 'transition': {'duration': 300, 'easing': 'quadratic-in-out'}}],
                'label': 'Play',
                'method': 'animate'
            },
            {
                'args': [[None], {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate',
                'transition': {'duration': 0}}],
                'label': 'Pause',
                'method': 'animate'
            }
        ],
        'direction': 'left',
        'pad': {'r': 10, 't': 87},
        'showactive': False,
        'type': 'buttons',
        'x': 0.1,
        'xanchor': 'right',
        'y': 0,
        'yanchor': 'top'
    }
]

sliders_dict = {
    'active': 0,
    'yanchor': 'top',
    'xanchor': 'left',
    'currentvalue': {
        'font': {'size': 20},
        'prefix': 'Year:',
        'visible': True,
        'xanchor': 'right'
    },
    'transition': {'duration': 300, 'easing': 'cubic-in-out'},
    'pad': {'b': 10, 't': 50},
    'len': 0.9,
    'x': 0.1,
    'y': 0,
    'steps': []
}

# make data
year = 2005
for country in countries:
    dataset_by_year = dataset[dataset['year'] == year]
    dataset_by_year_and_count = dataset_by_year[dataset_by_year['country'] == country]
    data_dict = {
        'x': list(dataset_by_year_and_count['GDP per capita']),
        'y': list(dataset_by_year_and_count['Life satisfaction in Cantril Ladder (World Happiness Report 2017)']),
        'mode': 'markers',
        'text': list(dataset_by_year_and_count['country']),
        'marker': {
            'sizemode': 'area',
            'sizeref': 200000,
            'size': list(dataset_by_year_and_count['Total population (Gapminder)'])
        },
        'name': country

    }
    figure['data'].append(data_dict)

# make frames
for year in years:
    frame = {'data': [], 'name': str(year)}
    for country in countries:
        dataset_by_year = dataset[dataset['year'] == int(year)]
        dataset_by_year_and_cont = dataset_by_year[dataset_by_year['country'] == country]

        data_dict = {
        'x': list(dataset_by_year_and_count['GDP per capita']),
        'y': list(dataset_by_year_and_count['Life satisfaction in Cantril Ladder (World Happiness Report 2017)']),
            'mode': 'markers',
            'text': list(dataset_by_year_and_count['country']),
            'marker': {
                'sizemode': 'area',
                'sizeref': 200000,
                'size': list(dataset_by_year_and_count['Total population (Gapminder)'])
            },
        'name': country

        }
        frame['data'].append(data_dict)

    figure['frames'].append(frame)
    slider_step = {'args': [
        [year],
        {'frame': {'duration': 300, 'redraw': False},
         'mode': 'immediate',
       'transition': {'duration': 300}}
     ],
     'label': year,
     'method': 'animate'}
    sliders_dict['steps'].append(slider_step)



figure['layout']['sliders'] = [sliders_dict]

iplot(figure, config=config)

The issue here is that I get an empty plot (slider, layout, axis labels animation are working) with no data at all, and no error is raised. So I honestly don't know where the problem is. It obviously has something to do with the data building in the script but I don't know what precisely.

Wrongful answered 16/8, 2017 at 11:3 Comment(3)
can you provide a mockup dataframe with 2 lines for each year in the dataset, df = pd.DataFrame({'county': ['Cochice', 'Pima', 'Santa Cruz', 'Maricopa', 'Yuma'], 'year': [2012, 2012, 2013, 2014, 2014], 'reports': [4, 24, 31, 2, 3]}), refer the example but put some sample data and the columns the same as your file and add it to the questionAbidjan
I just edited the question, thanks. By having a look at the file I think it may depend on the fact that not every country has data for all of the specified years, but I don't know how plotly handles this and, in case, how to make it work (for example by making the country datapoint just disappear if there's no data available for that year).Wrongful
In fact if I change the starting year to 2008, the legend gets populated (with each country, for every year it is present in the dataset - this is something I will handle later) and one datapoint appears in the plot: the one for Zimbabwe 2008. And it does not change throughout the animation. So it definitely depends on the way data is read.Wrongful
A
1

I used the sample data you gave and worked on it, i've encounted few issues and added my comments on each line, the main reasons were pointed out in the comments section, but you can cross check my code with yours and get what you need

Code:

from plotly.offline import init_notebook_mode, iplot
from IPython.display import display, HTML

import pandas as pd

init_notebook_mode(connected=True)

url = 'testing.csv'
dataset = pd.read_csv(url)
# instead of hardcoding you can use unique() function to get the years present in the file, then convert to list and sort based on years
# years = dataset['year'].unique().tolist()
# years.sort()
years = ['2007','2008','2009','2010','2011','2012','2013'] # try to provide years that contain data in the data set

# make list of continents
countries = []
for country in dataset['country'].unique():
    countries.append(country)
# make figure
figure = {
    'data': [],
    'layout': {},
    'frames': []
}
config = {'scrollzoom': True}

# fill in most of layout

# there is a small ranging issue, where some points go out of the plot so try this code if you notice it

#figure['layout']['xaxis'] = {'title': 'GDP per Capita',  'autorange': False, 'range': [int(dataset['GDP per capita'].min()), int(dataset['GDP per capita'].max())]} #was not set properly
#figure['layout']['yaxis'] = {'title': 'Life Expectancy', 'autorange': False, 
#                             'range': [int(dataset['Life satisfaction in Cantril Ladder (World Happiness Report 2017)'].min()), 
#                                    int(dataset['Life satisfaction in Cantril Ladder (World Happiness Report 2017)'].max())]} #was not set properly


figure['layout']['xaxis'] = {'title': 'GDP per Capita', 'type': 'log', 'autorange': True} #was not set properly
figure['layout']['yaxis'] = {'title': 'Life Expectancy', 'autorange': True} #was not set properly
figure['layout']['hovermode'] = 'closest'
figure['layout']['showlegend'] = True
figure['layout']['sliders'] = {
    'args': [
        'slider.value', {
            'duration': 400,
            'ease': 'cubic-in-out'
        }
    ],
    'initialValue': '2007',
    'plotlycommand': 'animate',
    'values': years,
    'visible': True
}
figure['layout']['updatemenus'] = [
    {
        'buttons': [
            {
                'args': [None, {'frame': {'duration': 500, 'redraw': False},
                         'fromcurrent': True, 'transition': {'duration': 300, 'easing': 'quadratic-in-out'}}],
                'label': 'Play',
                'method': 'animate'
            },
            {
                'args': [[None], {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate',
                'transition': {'duration': 0}}],
                'label': 'Pause',
                'method': 'animate'
            }
        ],
        'direction': 'left',
        'pad': {'r': 10, 't': 87},
        'showactive': False,
        'type': 'buttons',
        'x': 0.1,
        'xanchor': 'right',
        'y': 0,
        'yanchor': 'top'
    }
]

sliders_dict = {
    'active': 0,
    'yanchor': 'top',
    'xanchor': 'left',
    'currentvalue': {
        'font': {'size': 20},
        'prefix': 'Year:',
        'visible': True,
        'xanchor': 'right'
    },
    'transition': {'duration': 300, 'easing': 'cubic-in-out'},
    'pad': {'b': 10, 't': 50},
    'len': 0.9,
    'x': 0.1,
    'y': 0,
    'steps': []
}

# make data -  here you need to specify the year being used as starting point, important to change
year = 2007
for country in countries:
    dataset_by_year = dataset[dataset['year'] == year]
    dataset_by_year_and_cont=dataset_by_year[dataset_by_year['country'] == country]

    data_dict = {
        'x': list(dataset_by_year_and_cont['GDP per capita']),
        'y': list(dataset_by_year_and_cont['Life satisfaction in Cantril Ladder (World Happiness Report 2017)']),
        'mode': 'markers',
        'text': [country], # since there is only one country we do not need to provide the list for text -  
        #Suggestion: No need to have this
        'marker': {
            'sizemode': 'area',
            'sizeref': 200000,
            'size': list(dataset_by_year_and_cont['Total population (Gapminder)'])
        },
        'name': country
    }
    figure['data'].append(data_dict)

# make frames
for year in years:
    frame = {'data': [], 'name': str(year)}
    dataset_by_year = dataset[dataset['year'] == int(year)] # here this has been moved because if the country 
    # is not present for that particular year there is no need to plot those traces
    for country in dataset_by_year['country']:

        dataset_by_year_and_cont=dataset_by_year[dataset_by_year['country'] == country]

        data_dict = {
            'x': list(dataset_by_year_and_cont['GDP per capita']),
            'y': list(dataset_by_year_and_cont['Life satisfaction in Cantril Ladder (World Happiness Report 2017)']),
            'mode': 'markers',
            'text': [country], # since there is only one country we do not need to provide the list for text - 
            #Suggestion: No need to have this
            'marker': {
                'sizemode': 'area',
                'sizeref': 200000,
                'size': list(dataset_by_year_and_cont['Total population (Gapminder)'])
            },
            'name': country,
            'type': 'scatter',
            'showlegend': True
        }
        frame['data'].append(data_dict)

    figure['frames'].append(frame) #this block was indented and should not have been.
    slider_step = {'args': [
        [year],
        {'frame': {'duration': 300, 'redraw': False},
         'mode': 'immediate',
       'transition': {'duration': 300}}
     ],
     'label': year,
     'method': 'animate'}
    sliders_dict['steps'].append(slider_step)


figure['layout']['sliders'] = [sliders_dict]

iplot(figure, config=config)

Output: enter image description here

Abidjan answered 16/8, 2017 at 15:30 Comment(2)
I appreciate your help a lot. It works, and with some minor adjustment it will be perfect. I am studying every single change you made to sort out what you did and why you did it. You helped me a lot in climbing my plotly learning curve, today :) thanks a lot!Wrongful
@Wrongful Glad to help, Plotly is an awesome library, we just need to go through the reference guide and get used to the parameters :)Abidjan

© 2022 - 2024 — McMap. All rights reserved.