3
votes

Pretty new to python and programming in general so bear with me please. I have a data set imported from a .csv file and I'm trying to plot a column of values (y axis) by date (x axis) over a 1 year period but the problem is that the dates are way too dense and I can't for the life of me figure out how to space them out or modify how they're defined. Here's the code I'm working with:

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib as mpl
from scipy import stats
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt

df = pd.read_csv('Vanuatu Earthquakes 2018-2019.csv')

and here's the line plot code:

plt.figure(figsize=(15, 7))
ax = sns.lineplot(x='date', y='mag', data=df).set_title("Earthquake magnitude May 2018-2019")

plt.xlabel('Date')
plt.ylabel('Magnitude (Mw)')
plt.savefig('EQ mag time')

This currently gives me this line plot: 1

Currently I want to do it by something like a small tick for each day and a larger tick + label for the beginning of each week. Doesn't have to be exactly that but I'm mostly looking to just decrease the density. I've looked at loads of posts on here but none of them seem to work for my situation so any help would be greatly appreciated.

[Update]

Got the dates working as per Konqui's advice below and my code now looks like this:

time = pd.date_range(start = '01-05-2018',
                     end = '01-05-2019',
                     freq = 'D')
df = pd.DataFrame({'date': list(map(lambda x: str(x), time)),
                   'mag': np.random.random(len(time))})

plt.figure(figsize=(15, 7))
df['date'] = pd.to_datetime(df['date'], format = '%Y-%m')
ax = sns.lineplot(x='date', y='mag', data=df).set_title("Earthquake magnitude May 2018-2019")
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday = 1))
ax.xaxis.set_major_formatter(md.DateFormatter('%Y-%m-%d'))
plt.setp(ax.xaxis.get_majorticklabels(), rotation = 90)
ax.xaxis.set_minor_locator(md.DayLocator(interval = 1))
plt.xlabel('Date')
plt.ylabel('Magnitude (Mw)')

which gives me an error message: AttributeError: 'Text' object has no attribute 'xaxis'. Any thoughts?

1

1 Answers

5
votes

Assumption

I suppose you start from a dataframe similar to this one saved in a Vanuatu Earthquakes 2018-2019.csv file :

import pandas as pd
import numpy as np

time = pd.date_range(start = '01-01-2020',
                     end = '31-03-2020',
                     freq = 'D')
df = pd.DataFrame({'date': list(map(lambda x: str(x), time)),
                   'mag': np.random.random(len(time))})

output:

                  date       mag
0  2020-01-01 00:00:00  0.940040
1  2020-01-02 00:00:00  0.765570
2  2020-01-03 00:00:00  0.951839
3  2020-01-04 00:00:00  0.708172
4  2020-01-05 00:00:00  0.705032
5  2020-01-06 00:00:00  0.857500
6  2020-01-07 00:00:00  0.866418
7  2020-01-08 00:00:00  0.363287
8  2020-01-09 00:00:00  0.289615
9  2020-01-10 00:00:00  0.741499

plotting:

import seaborn as sns
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize = (15, 7))

sns.lineplot(ax = ax, x='date', y='mag', data=df).set_title('Earthquake magnitude May 2018-2019')

plt.xlabel('Date')
plt.ylabel('Magnitude (Mw)')

plt.show()

enter image description here


Answer

You should do a series of things:

  1. First of all, you get that density of labels because your 'date' values are str type, you need to convert them to datetime by

    df['date'] = pd.to_datetime(df['date'], format = '%Y-%m-%d')
    

    in this way your x axis is a datetime type and the above plot will become this:

enter image description here

  1. Then you have to adjust ticks; for the major ticks you should set:

    import matplotlib.dates as md
    
    # specify the position of the major ticks at the beginning of the week
    ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday = 1))
    # specify the format of the labels as 'year-month-day'
    ax.xaxis.set_major_formatter(md.DateFormatter('%Y-%m-%d'))
    # (optional) rotate by 90° the labels in order to improve their spacing
    plt.setp(ax.xaxis.get_majorticklabels(), rotation = 90)
    

    and for the minor ticks:

    # specify the position of the minor ticks at each day
    ax.xaxis.set_minor_locator(md.DayLocator(interval = 1))
    

    optionally, you can edit the length of the ticks with:

    ax.tick_params(axis = 'x', which = 'major', length = 10)
    ax.tick_params(axis = 'x', which = 'minor', length = 5)
    

    so the final plot will become:

enter image description here


Complete Code

# import required packages
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.dates as md

# read the dataframe
df = pd.read_csv('Vanuatu Earthquakes 2018-2019.csv')
# convert 'date' column type from str to datetime
df['date'] = pd.to_datetime(df['date'], format = '%Y-%m-%d')

# prepare the figure
fig, ax = plt.subplots(figsize = (15, 7))

# set up the plot
sns.lineplot(ax = ax, x='date', y='mag', data=df).set_title('Earthquake magnitude May 2018-2019')

# specify the position of the major ticks at the beginning of the week
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday = 1))
# specify the format of the labels as 'year-month-day'
ax.xaxis.set_major_formatter(md.DateFormatter('%Y-%m-%d'))
# (optional) rotate by 90° the labels in order to improve their spacing
plt.setp(ax.xaxis.get_majorticklabels(), rotation = 90)

# specify the position of the minor ticks at each day
ax.xaxis.set_minor_locator(md.DayLocator(interval = 1))

# set ticks length
ax.tick_params(axis = 'x', which = 'major', length = 10)
ax.tick_params(axis = 'x', which = 'minor', length = 5)

# set axes labels
plt.xlabel('Date')
plt.ylabel('Magnitude (Mw)')

# show the plot
plt.show()

Notes

If you pay attention to the y axis in my plots, you see that 'mag' values fall in the range (0-1). This is due to the fact that I generate this fake data with 'mag': np.random.random(len(time)). If you read your data from the file Vanuatu Earthquakes 2018-2019.csv, you will get the correct values on the y axis. Try to simply copy the code in the Whole code section.