I have seen multiple, multiple questions about how to stop a matplotlib figure from staying open and blocking other code from working. None of the accepted answers have worked for me. I have tried:
plot.show(block=False)
plot.close()
plot.close('all')
plot.close(fig)
The only thing that works is plot.show(block=False)
, however it closes the graph as soon as it is open.
Next, I tried using plot.show(block=False)
followed by a request for input using input("Press enter to continue")
to delay the closing of the plot. This only resulted in a blank figure being displayed, which does actually close when I hit enter.
Normally, just closing the plot window with the 'x' button does the trick just fine. However, in this case when I close the window the program just hangs until I tell my IDE to shut it down.
EDIT: I have also tried the solution given in the comments. It is the worst of all the offendors because it renders my computer frozen unless I force close the plot.
Here is the code that is producing the behavior:
import birth_names as bn
import matplotlib.pyplot as plt
def plotRankAndScores(name, gender):
files = bn.getPaths()
x1, y1 = bn.getAllRanks(name, gender, files)
x2, y2 = bn.getAllScores(name, gender, files)
ave = bn.getAverageRank(name, gender, select=False, filez=files)
fig2, (ax3, ax4) = plt.subplots(2, 1, sharex='all', figsize=(10, 10))
plt.xlabel("Year")
ax3.plot(x1, y1, 'b')
ax3.set_ylabel("Rank")
ax3.axhline(y1.mean(), label='average = {}'.format(ave), linestyle='--', color='red')
ax3.legend()
ax4.plot(x2, y2, 'b')
ax4.set_ylabel("Number of Births")
ax4.axhline(y2.mean(), label='average = {}'.format(y2.mean()), linestyle='--', color='red')
ax4.legend()
plt.suptitle("Name Rank and Number of Births by Year")
plt.show(block=False)
if __name__ == '__main__':
plotRankAndScores("Wesley", "M")
input("Press Enter to end display")
print("All Done!")
And, in case the problem is not related to my use of matplotlib, I will provide the code for birth_names.py. I have deleted all the functions that birthplots.py does not use. Unfortunately it is still a long list of functions:
import tkinter as tk
import os
import csv
import numpy as np
def parseFileYear(file_name):
"""
:param file_name: file name in the format yobXXXX.csv where XXXX is the four digit year.
:return: the year
"""
return file_name[3:7]
def totalScore(path):
"""
Reads the CSV file at location 'path' and prints out the number of boys and girls born
along with the number of unique boy names and unique girl names.
:param path: The CSV file containing birth name data
:return:
"""
boys = 0
girls = 0
boyNames = 0
girlNames = 0
with open(path, 'r') as csvFile:
reader = csv.reader(csvFile)
for row in reader:
if row[1] == "M":
boys += int(row[2])
boyNames += 1
else:
girls += int(row[2])
girlNames += 1
totalNames = boyNames + girlNames
print("{} boys were born with {} unique names".format(boys, boyNames))
print("{} girls were born with {} unique names".format(girls, girlNames))
print("Total unique names: ", totalNames, '\n')
def getScore(year, name, gender):
file_name = "yob{}.csv".format(year)
curr_path = os.path.dirname(__file__)
abs_path = curr_path + "/us_babynames_by_year/{}".format(file_name)
with open(abs_path, 'r') as CSVdat:
for row in csv.reader(CSVdat):
if row[1] == gender and row[0] == name:
return row[2]
return -1
def getRank(year, name, gender):
"""
This method takes a year, name and gender and returns the rank of that name in the given year. If the name does not
exist, the method returns -1
:param year:
:param name:
:param gender:
:return:
"""
file_name = "yob{}.csv".format(year)
curr_path = os.path.dirname(__file__)
abs_path = curr_path + "/us_babynames_by_year/{}".format(file_name)
last_count = 0
rank = 0
name_found = False
with open(abs_path, 'r') as CSVdat:
for row in csv.reader(CSVdat):
if row[1] == gender:
name_count = int(row[2])
if name_count == last_count:
last_count = name_count
else:
rank += 1
last_count = name_count
if row[0] == name:
name_found = True
break
if not name_found:
return -1
return rank
def getAverageRank(name, gender, select=True, filez=None):
"""
return the average rank of a given name over the selected files. If using
this method in conjunction with others that are analyzing multiple CSV
files, it is prudent to use select=False, and pass an iterable containing
the absolute paths of the files being looked at. This will save you from
dealing with multiple pop-up file selection dialogues.
:param name:
:param gender:
:param select: default True. If True, pop up file selection dialogue.
If false, the files argument must not be empty.
:param files: default None. An iterable containing the absolute paths
to the CSV files being analyzed. This parameter is required if param
select is False.
:return:
"""
if select:
files = getPaths()
else:
files = filez
name_found = False
rank_sum = 0
count = len(files)
for file in files:
file_name = os.path.basename(file)
year = parseFileYear(file_name)
rank = getRank(year, name, gender)
if rank != -1:
name_found = True
rank_sum += rank
if name_found:
return rank_sum / count
return -1
def getAllRanks(name, gender, files):
"""
Returns a numpy 2-D array containing the year and rank pairs. Useful for plotting visualizations
:param name:
:param gender:
:param files:
:return:
"""
dim = len(files)
year_data = np.zeros(dim, dtype=int)
rank_data = np.zeros(dim, dtype=int)
for i, file in enumerate(files):
file_name = os.path.basename(file)
year = parseFileYear(file_name)
rank = getRank(year, name, gender)
if rank == -1: # clean -1 from data (case of name not found in file)
rank = 0
year_data[i] = year
rank_data[i] = rank
return year_data, rank_data
def getPaths():
root = tk.Tk()
root.withdraw()
return tk.filedialog.askopenfilenames()
def getAllScores(name, gender, files):
dim = len(files)
year_data = np.zeros(dim, dtype=int)
score_data = np.zeros(dim, dtype=int)
for i, file in enumerate(files):
year = parseFileYear(os.path.basename(file))
score = getScore(year, name, gender)
year_data[i] = year
score_data[i] = score
return year_data, score_data