3

I'm trying to remake an existing animated line graph I made where each line has a uniquely scaled y-axis - one on the left, one on the right. The graph is comparing the value of two cryptocurrencies that have vastly different sizes (eth/btc), which is why I need multiple scales to actually see changes.

My data has been formatted in a pd df (numbers here are random):

                   Date  ETH Price     BTC Price
0   2020-10-30 00:00:00   0.155705  1331.878496
1   2020-10-31 00:00:00   0.260152  1337.174272
..                  ...        ...           ...
290 2021-08-15 16:42:09   0.141994  2846.719819
[291 rows x 3 columns]

And code is roughly:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as ani

color = ['cyan', 'orange', 'red']
fig = plt.figure()
plt.xticks(rotation=45, ha="right", rotation_mode="anchor") 
plt.subplots_adjust(bottom = 0.2, top = 0.9) 
plt.ylabel('Coin Value (USD)')
plt.xlabel('Date')

def buildChart(i=int):
    df1 = df.set_index('Date', drop=True)
    plt.legend(["ETH Price", "BTC Price"])
    p = plt.plot(df1[:i].index, df1[:i].values) 
    for i in range(0,2):
        p[i].set_color(color[i])

animator = ani.FuncAnimation(fig, buildChart, interval = 10)
plt.show()

Resulting Animation

I tried to create a second axis with a twin x to the first axis.

color = ['cyan', 'orange', 'blue']
fig, ax1 = plt.subplots() #Changes over here
plt.xticks(rotation=45, ha="right", rotation_mode="anchor") 
plt.subplots_adjust(bottom = 0.2, top = 0.9) 
plt.ylabel('Coin Value (USD)')
plt.xlabel('Date')

def buildChart(i=int):
    df1 = df.set_index('Date', drop=True)
    plt.legend(["ETH Price", "Bitcoin Price"])
    data1 = df1.iloc[:i, 0:1] # Changes over here
    # ------------- More Changes Start
    ax2 = ax1.twinx() 
    ax2.set_ylabel('Cost of Coin (USD)') 
    data2 = df1.iloc[:i, 1:2] 
    ax2.plot(df1[:i].index, data2)
    ax2.tick_params(axis='y')
    # -------------- More Changes End
    p = plt.plot(df1[:i].index, data1) 
    for i in range(0,1):
        p[i].set_color(color[i])

import matplotlib.animation as ani
animator = ani.FuncAnimation(fig, buildChart, interval = 10)
plt.show()

Resulting Animation After Changes

Current issues:

  • X-Axis start at ~1999 rather than late 2020 ---- Causes all changes on the y-axis to be a nearly vertical line
  • Left Y-Axis label is on a scale of 0-1?
  • Right y-axis labels are recurring, overlapping, moving.

I believe my approach to making a second scale must have been wrong to get this many errors, but this seems like the way to do it.

1 Answer 1

2

I re-structured your code in order to easily set up a secondary axis animation.
Here the code of the animation with a single y axis:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation


df = pd.DataFrame({'date': pd.date_range(start = '2020-01-01', end = '2020-04-01', freq = 'D')})
df['ETH'] = 2*df.index + 300 + 100*np.random.randn(len(df))
df['BTC'] = 5*df.index + 13000 + 200*np.random.randn(len(df))


def update(i):
    ax.cla()

    ax.plot(df.loc[:i, 'date'], df.loc[:i, 'ETH'], label = 'ETH Price', color = 'red')
    ax.plot(df.loc[:i, 'date'], df.loc[:i, 'BTC'], label = 'BTC Price', color = 'blue')

    ax.legend(frameon = True, loc = 'upper left', bbox_to_anchor = (1.15, 1))

    ax.set_ylim(0.9*min(df['ETH'].min(), df['BTC'].min()), 1.1*max(df['ETH'].max(), df['BTC'].max()))

    ax.tick_params(axis = 'x', which = 'both', top = False)
    ax.tick_params(axis = 'y', which = 'both', right = False)

    plt.setp(ax.xaxis.get_majorticklabels(), rotation = 45)

    ax.set_xlabel('Date')
    ax.set_ylabel('ETH Coin Value (USD)')

    plt.tight_layout()


fig, ax = plt.subplots(figsize = (6, 4))

ani = FuncAnimation(fig = fig, func = update, frames = len(df), interval = 100)

plt.show()

enter image description here

Starting from the code above, you should twin the axis out of the update function: if you keep ax.twinx() inside the function, this operation will be repeated in each iteration and you will get a new axis each time.
Below the code for an animation with a secondary axis:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation


df = pd.DataFrame({'date': pd.date_range(start = '2020-01-01', end = '2020-04-01', freq = 'D')})
df['ETH'] = 2*df.index + 300 + 100*np.random.randn(len(df))
df['BTC'] = 5*df.index + 13000 + 200*np.random.randn(len(df))


def update(i):
    ax1.cla()
    ax2.cla()

    line1 = ax1.plot(df.loc[:i, 'date'], df.loc[:i, 'ETH'], label = 'ETH Price', color = 'red')
    line2 = ax2.plot(df.loc[:i, 'date'], df.loc[:i, 'BTC'], label = 'BTC Price', color = 'blue')

    lines = line1 + line2
    labels = [line.get_label() for line in lines]
    ax1.legend(lines, labels, frameon = True, loc = 'upper left', bbox_to_anchor = (1.15, 1))

    ax1.set_ylim(0.9*df['ETH'].min(), 1.1*df['ETH'].max())
    ax2.set_ylim(0.9*df['BTC'].min(), 1.1*df['BTC'].max())

    ax1.tick_params(axis = 'x', which = 'both', top = False)
    ax1.tick_params(axis = 'y', which = 'both', right = False, colors = 'red')
    ax2.tick_params(axis = 'y', which = 'both', right = True, labelright = True, left = False, labelleft = False, colors = 'blue')

    plt.setp(ax1.xaxis.get_majorticklabels(), rotation = 45)

    ax1.set_xlabel('Date')
    ax1.set_ylabel('ETH Coin Value (USD)')
    ax2.set_ylabel('BTC Coin Value (USD)')

    ax1.yaxis.label.set_color('red')
    ax2.yaxis.label.set_color('blue')

    ax2.spines['left'].set_color('red')
    ax2.spines['right'].set_color('blue')

    plt.tight_layout()


fig, ax1 = plt.subplots(figsize = (6, 4))
ax2 = ax1.twinx()

ani = FuncAnimation(fig = fig, func = update, frames = len(df), interval = 100)

plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.