|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# coding=utf-8 |
| 3 | +# vim:ts=4:sts=4:sw=4:et |
| 4 | +# |
| 5 | +# Author: Hari Sekhon |
| 6 | +# Date: 2025-04-26 00:37:28 +0800 (Sat, 26 Apr 2025) |
| 7 | +# |
| 8 | +# https///github.com/HariSekhon/DevOps-Python-tools |
| 9 | +# |
| 10 | +# License: see accompanying Hari Sekhon LICENSE file |
| 11 | +# |
| 12 | +# If you're using my code you're welcome to connect with me on LinkedIn |
| 13 | +# and optionally send me feedback to help steer this or other code I publish |
| 14 | +# |
| 15 | +# https://www.linkedin.com/in/HariSekhon |
| 16 | +# |
| 17 | + |
| 18 | +""" |
| 19 | +
|
| 20 | +Plots the Marriage rates in England & Wales using the latest download from the UK Government website: |
| 21 | +
|
| 22 | + https://www.ons.gov.uk/peoplepopulationandcommunity/birthsdeathsandmarriages/marriagecohabitationandcivilpartnerships/bulletins/marriagesinenglandandwalesprovisional/2021and2022 |
| 23 | +
|
| 24 | +""" |
| 25 | + |
| 26 | +from __future__ import absolute_import |
| 27 | +from __future__ import division |
| 28 | +from __future__ import print_function |
| 29 | +from __future__ import unicode_literals |
| 30 | + |
| 31 | +import os |
| 32 | +import platform |
| 33 | +import subprocess |
| 34 | +import sys |
| 35 | +#import time |
| 36 | +import traceback |
| 37 | +import pandas as pd |
| 38 | +import matplotlib.pyplot as plt |
| 39 | +srcdir = os.path.abspath(os.path.dirname(__file__)) |
| 40 | +libdir = os.path.join(srcdir, 'pylib') |
| 41 | +sys.path.append(libdir) |
| 42 | +try: |
| 43 | + # pylint: disable=wrong-import-position |
| 44 | + from harisekhon.utils import log |
| 45 | + from harisekhon import CLI |
| 46 | +except ImportError as _: |
| 47 | + print(traceback.format_exc(), end='') |
| 48 | + sys.exit(4) |
| 49 | + |
| 50 | +__author__ = 'Hari Sekhon' |
| 51 | +__version__ = '0.1' |
| 52 | + |
| 53 | + |
| 54 | +# pylint: disable=too-few-public-methods |
| 55 | +class PlotUKMarriageRates(CLI): |
| 56 | + |
| 57 | + def __init__(self): |
| 58 | + # Python 2.x |
| 59 | + super(PlotUKMarriageRates, self).__init__() |
| 60 | + # Python 3.x |
| 61 | + # super().__init__() |
| 62 | + self.timeout_default = 0 |
| 63 | + |
| 64 | + # def add_options(self): |
| 65 | + # super(PlotUKMarriageRates, self).add_options() |
| 66 | + # |
| 67 | + # def process_options(self): |
| 68 | + # super(PlotUKMarriageRates, self).process_options() |
| 69 | + |
| 70 | + def run(self): |
| 71 | + if not self.args: |
| 72 | + self.usage("Provide path to datadownload.xlsx") |
| 73 | + |
| 74 | + file_path = self.args[0] |
| 75 | + |
| 76 | + if not os.path.isfile(file_path): |
| 77 | + self.usage(f"Invalid argument provided, not a file: {file_path}") |
| 78 | + |
| 79 | + log.info(f"Loading file: {file_path}") |
| 80 | + xls = pd.ExcelFile(file_path) |
| 81 | + |
| 82 | + log.info("Parsing xls") |
| 83 | + # Read the relevant sheet and skip the metadata |
| 84 | + df = xls.parse('Figure 2', skiprows=6) |
| 85 | + |
| 86 | + # Set proper column headers |
| 87 | + df.columns = df.iloc[1] |
| 88 | + df = df[2:] # Drop the header rows |
| 89 | + |
| 90 | + # Rename columns for clarity |
| 91 | + df.columns = ['Year', 'Opposite-sex Men', 'Opposite-sex Women', 'Same-sex Men', 'Same-sex Women'] |
| 92 | + |
| 93 | + # Convert data types |
| 94 | + df['Year'] = df['Year'].astype(int) |
| 95 | + for col in ['Opposite-sex Men', 'Opposite-sex Women', 'Same-sex Men', 'Same-sex Women']: |
| 96 | + df[col] = pd.to_numeric(df[col], errors='coerce') |
| 97 | + |
| 98 | + log.info("Plotting") |
| 99 | + plt.figure(figsize=(10, 6)) |
| 100 | + plt.plot(df['Year'], df['Opposite-sex Men'], label='Opposite-sex Men') |
| 101 | + plt.plot(df['Year'], df['Opposite-sex Women'], label='Opposite-sex Women') |
| 102 | + plt.plot(df['Year'], df['Same-sex Men'], label='Same-sex Men') |
| 103 | + plt.plot(df['Year'], df['Same-sex Women'], label='Same-sex Women') |
| 104 | + |
| 105 | + plt.title('Marriage Rates Over Time (England & Wales)') |
| 106 | + plt.xlabel('Year') |
| 107 | + plt.ylabel('Marriage Rate per 1,000 People') |
| 108 | + plt.legend() |
| 109 | + plt.grid(True) |
| 110 | + plt.tight_layout() |
| 111 | + #plt.show() |
| 112 | + # |
| 113 | + # doesn't show anything because the script finishes before the GUI event loop |
| 114 | + # has time to process and display the window |
| 115 | + #plt.show(block=False) |
| 116 | + # |
| 117 | + # pylint: disable=line-too-long |
| 118 | + # |
| 119 | + # doesn't work even with this hack: |
| 120 | + # |
| 121 | + # WARNING: NSWindow geometry should only be modified on the main thread! This will raise an exception in the future |
| 122 | + # |
| 123 | + #import threading |
| 124 | + #threading.Thread(target=plt.show).start() |
| 125 | + #sleep_secs = 20 |
| 126 | + #log.info(f"Sleeping for {sleep_secs} secs to allow you to see the graph pop-up") |
| 127 | + #time.sleep(sleep_secs) |
| 128 | + |
| 129 | + image_path = os.path.splitext(file_path)[0] + '.png' |
| 130 | + log.info("Generating output image: {image_path}") |
| 131 | + |
| 132 | + #if os.path.exists(image_path): |
| 133 | + # log.warning(f"Image page already exists, skipping recreating for safety: {image_path}") |
| 134 | + # #plt.close() |
| 135 | + # return |
| 136 | + |
| 137 | + # doesn't solve blank png |
| 138 | + #plt.gcf().canvas.draw() |
| 139 | + |
| 140 | + # results in blank png |
| 141 | + #plt.savefig(image_path) |
| 142 | + |
| 143 | + fig = plt.gcf() # draw before saving |
| 144 | + fig.canvas.draw() # force rendering |
| 145 | + plt.savefig(image_path) |
| 146 | + |
| 147 | + plt.show() |
| 148 | + |
| 149 | + if platform.system() == "Darwin": |
| 150 | + # fire and forget |
| 151 | + # pylint: disable=subprocess-run-check |
| 152 | + subprocess.run(['open', image_path]) |
| 153 | + |
| 154 | + #plt.close() |
| 155 | + |
| 156 | + |
| 157 | +if __name__ == '__main__': |
| 158 | + PlotUKMarriageRates().main() |
0 commit comments