How to edit tables in python? - python

I have a code in python 3.x which uses matplotlib.
colLabels = ["Name", "Number"]
data = [["Peter", 17], ["Sara", 21], ["John", 33]]
the_table = ax.table(cellText=data,
colLabels=colLabels,
loc='center')
plt.pause(0.1)
The above code is in a loop, now I want to search for the row with "Peter" in first column (it's unique) and edit it so that in every iteration the entry in second column changes. I could clear whole ax and add new table but it's inefficient (I would be redrawing table with multiple rows every 0.1s)
Is there a way to edit this in matplotlib (and how) or should I use some other library (which)?

The text in a matplotlib table can be updated by chosing the cell and set the text of the cell's _text attribute. E.g.
the_table.get_celld()[(2, 1)].get_text().set_text("new text")
will update the cell in the third row and second column.
An animated example:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots(figsize=(4,2))
colLabels = ["Name", "Number"]
data = [["Peter", 1], ["Sara", 1], ["John", 1]]
the_table = ax.table(cellText=data,
colLabels=colLabels,
loc='center')
def update(i):
the_table.get_celld()[(1, 1)].get_text().set_text(str(i))
the_table.get_celld()[(2, 1)].get_text().set_text(str(i*2))
the_table.get_celld()[(3, 1)].get_text().set_text(str(i*3))
ani = FuncAnimation(fig, update, frames=20, interval=400)
plt.show()
Finding out which cell needs to be updated, would probably best be done using the data instead of reading it from the table.
inx = list(zip(*data))[0].index("Peter")
gives you the index 0, such that the cell can be accessed via
the_table.get_celld()[(inx+1, 1)] (note the +1, which is there because of the table headline).

Related

Replacing data position in table - Matplotlib

i'm trying to create a table with below result in matplotlib , data extracted from MysQl.
i used theses charts inside An application built by PYQT5
but unfortunately this is what i get :
code used :
def dashboard_fleet_statistics_fleets_visisted_current_year(self):
try:
mydb = con.connect(host= "localhost", user ="root", password='''''', db="fleet")
cursor = mydb.cursor()
cursor.execute('''SELECT (fleet_code) ,fleet_name,COUNT(fleet_code) AS "No Of Visits",(Year(date_of_visit)) AS "Year Of Visit"
FROM vehicle_tyre_parameters
WHERE fleet_code != "" AND Year(date_of_visit)= Year(curdate())
GROUP BY fleet_code''')
result4 = cursor.fetchall()
print(list(result4))
fleet_code=[]
fleet_name=[]
no_of_visits =[]
fleet_year=[]
for row in result4 :
fleet_code.append(row[0])
fleet_name.append(row[1])
no_of_visits.append(row[2])
fleet_year.append(row[3])
print(list(fleet_code))
print(list(fleet_name))
print(list(no_of_visits))
print(list(fleet_year))
fig, ax = plt.subplots()
values=[fleet_code,fleet_name,no_of_visits,fleet_year]
table = ax.table(cellText=values,rowLabels=['Fleet Code','Fleet Name','No of Visits','Year of Visit'] ,colWidths=[.5,.5],colLoc='center',loc='center',bbox=[-0.3, 1, 1, 0.275])
#modify table
table.set_fontsize(14)
table.scale(1,4)
ax.axis('off')
table[(1, 0)].set_facecolor("white")
table[(2, 0)].set_facecolor("gray")
plt.tight_layout()
#display table
plt.show()
Appreciate your help to get a table with 1st picture ! Thanks
I cannot really access your data, but implementing the example you attached is pretty straightforward. I first put all the information in a pandas dataframe, and then used this neat trick that allows you to convert a dataframe to a matplotlib table. The rest is really playing with the design.
Here is a code snippet:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Create figure
fig, ax = plt.subplots(figsize = (25,2))
fig.patch.set_visible(False)
ax.axis('off')
ax.axis('tight')
# Make some data and put in dataframe
data = [['FC-1', 'NAME', 5, 2021], ['FC-6', 'NAME', 5, 2021]]
df = pd.DataFrame(data, columns=['Fleet code', 'Fleet name', 'Number of visits', 'Year of visit'])
# Create the table from the dataframe:
tab = ax.table(cellText=df.values, colLabels=df.columns, loc='center', cellLoc='center',
colColours = ['lightsteelblue', 'lightsteelblue', 'lightsteelblue', 'lightsteelblue'],
cellColours = [['w','w','w','w'], ['lightgray','lightgray','lightgray','lightgray']])
# Design the table:
# Fonts:
tab.auto_set_font_size(False)
tab.set_fontsize(20)
tab.scale(2, 2)
fig.tight_layout()
plt.show()
Here is the end result:

How to plot a heatmap using seaborn or matplotlib?

I have a dataframe that I am trying to visualize into a heatmap, I used matplotlib to make a heatmap but it is showing data that is not apart of my dataframe.
I've tried to create a heatmap using matplotlib from an example I found online and changed the code to work for my data. But on the left side of the graph and top of it there are random values that are not apart of my data and I'm not sure how to remove them.
import pandas as pd
import numpy as np
import requests
from bs4 import BeautifulSoup
from io import StringIO
url = 'http://mcubed.net/ncaab/seeds.shtml'
#Getting the website text
data = requests.get(url).text
#Parsing the website
soup = BeautifulSoup(data, "html5lib")
#Create an empty list
dflist = []
#If we look at the html, we don't want the tag b, but whats next to it
#StringIO(b.next.next), takes the correct text and makes it readable to
pandas
for b in soup.findAll({"b"})[2:-1]:
dflist.append(pd.read_csv(StringIO(b.next.next), sep = r'\s+', header
= None))
dflist[0]
#Created a new list, due to the melt we are going to do not been able to
replace
#the dataframes in DFList
meltedDF = []
#The second item in the loop is the team number starting from 1
for df, teamnumber in zip(dflist, (np.arange(len(dflist))+1)):
#Creating the team name
name = "Team " + str(teamnumber)
#Making the team name a column, with the values in df[0] and df[1] in
our dataframes
df[name] = df[0] + df[1]
#Melting the dataframe to make the team name its own column
meltedDF.append(df.melt(id_vars = [0, 1, 2, 3]))
# Concat all the melted DataFrames
allTeamStats = pd.concat(meltedDF)
# Final cleaning of our new single DataFrame
allTeamStats = allTeamStats.rename(columns = {0:name, 2:'Record', 3:'Win
Percent', 'variable':'Team' , 'value': 'VS'})\
.reindex(['Team', 'VS', 'Record', 'Win
Percent'], axis = 1)
allTeamStats
#Graph visualization Making a HeatMap
%matplotlib inline
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
y=["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16"]
x=["16","15","14","13","12","11","10","9","8","7","6","5","4","3","2","1"]
winp = []
for i in x:
lst = []
for j in y:
percent = allTeamStats.loc[(allTeamStats["Team"]== 'Team '+i) &\
(allTeamStats["VS"]== "vs.#"+j)]['Win
Percent'].iloc[0]
percent = float(percent[:-1])
lst.append(percent)
winp.append(lst)
winpercentage= np.array([[]])
fig,ax=plt.subplots(figsize=(18,18))
im= ax.imshow(winp, cmap='hot')
# We want to show all ticks...
ax.set_xticks(np.arange(len(y)))
ax.set_yticks(np.arange(len(x)))
# ... and label them with the respective list entries
ax.set_xticklabels(y)
ax.set_yticklabels(x)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
rotation_mode="anchor")
# Loop over data dimensions and create text annotations.
for i in range(len(x)):
for j in range(len(y)):
text = ax.text(j, i, winp[i][j],
ha="center", va="center", color="red")
ax.set_title("Win Percentage of Each Matchup", fontsize= 40)
heatmap = plt.pcolor(winp)
plt.colorbar(heatmap)
ax.set_ylabel('Seeds', fontsize=40)
ax.set_xlabel('Seeds', fontsize=40)
plt.show()
The results I get are what I want except for the two lines that are on the left side and top of the heatmap. I'm unsure what these values are coming from and to easier see them I used cmap= 'hot' to show the values that are not supposed to be there. If you could help me fix my code to plot it correctly or plot an entire new heatmap using seaborn (my TA told me to try using seaborn but I've never used it yet) with my data. Anything helps Thanks!
I think the culprit is this line: im= ax.imshow(winp, cmap='hot') in your code. Delete it and try again. Basically, anything that you plotted after that line was laid over what that line created. The left and top "margins" were the only parts of the image on the bottom that you could see.

How to format values with comma separator in matplotlib table with an attached chart?

I have a pandas dataframe which I used the pandas.plot function to plot a bar chart. Within the function I set the table function to on. How can I format the values in this accompanying table with comma separators?
I am able to do these to the axis values, just not the accompanying table
I have already tried converting the values to float, but pandas plot only plots integers and therefore gives an error saying 'Empty Dataframe': no numeric data to plot.
ax1 = mydf.plot(kind='bar', title= chart with table, fontsize=8, width=0.75, legend=True, table=True)
ax1.legend(loc=5, bbox_to_anchor=(1.25,0.5), fontsize='x-small')
ax1.axes.get_xaxis().set_visible(False)
ax1.get_yaxis().get_major_formatter().set_scientific(False)
ax1.get_yaxis().set_major_formatter(ticker.StrMethodFormatter('${x:,.0f}'))
ax1.set_ylim(-10000000,10000000)
ax1.set_ylabel("P&L",fontsize=9)
ax1.axhline(0,0,1, color='k', linewidth=0.5)
table_ax1 = ax1.tables[0]
table_ax1.auto_set_font_size(False)
table_ax1.set_fontsize('8')
table_ax1.scale(1,2)
plt.tight_layout()
I don't know of a great way to force that formatting on the table ahead of time, without you explicitly making the matplotlib table yourself, however, you could iterate through the contents of the table and convert them this way instead (if you need to use the pandas implementation). I added some code in here from this related question demonstrating how the table can be manipulated as well.
import pandas as pd
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,1, figsize = (5,5))
df= pd.DataFrame({'City': ['LA', 'SF', 'Dallas'],
'Lakes': [10000, 90000, 600000], # lets set some that can be comma formatted
'Rivers': [1, 0, 0],
'State': ['CA', 'CA', 'TX'],
'Waterfalls': [200500, 450000, 50000]})
myplot = df.plot(x=['City','State'],kind='bar',stacked='True',table=True, ax =ax)
### you can also scale and change the table
### see https://stackoverflow.com/questions/39668665/format-a-table-that-was-added-to-a-plot-using-pandas-dataframe-plot
myplot.axes.get_xaxis().set_visible(False)
# Getting the table created by pandas and matplotlib
table = myplot.tables[0]
# Setting the font size
table.set_fontsize(12)
# Rescaling the rows to be more readable
table.scale(1,2)
## to format the table values I will retrieve them and change them when they aren't text labels
xtls = ax.get_xticklabels()
xtls = [i.get_text() for i in xtls]
ax.set_xticklabels([])
t = ax.tables[0]
for c in t.get_children():
tobj = c.get_text()
text = tobj.get_text()
if text not in xtls:
try: # some texts will be strings that are labels, we can't convert them
s = '{:0,d}'.format(int(text))
tobj.set_text(s)
except:
pass

How to make a 1-cell table headers with Matplotlib?

I am using Matplotlib's PdfPages to plot various figures and tables from queried data and generate a Pdf. I want to group plots by various sections such as "Stage 1", "Stage 2", and "Stage 3", by essentially creating section headers. For example, in a Jupyter notebook I can make cell's markdown and create bolded headers. However, I am not sure how to do something similar with PdfPages. One idea I had was to generate a 1 cell table containing the section title. Instead of creating a 1 cell table, it has a cell per character in the title.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12, 2))
ax = plt.subplot(111)
ax.axis('off')
tab = ax.table(cellText=['Stage 1'], bbox=[0, 0, 1, 1])
tab.auto_set_font_size(False)
tab.set_fontsize(24)
This results in the following output:
If anyone has suggestions for how to create section headers or at least fix the cell issue in the table I created, I would appreciate your input. Thanks!
You need to use colLabels to name the columns and use the cellText with a corresponding shape
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12, 2))
ax = plt.subplot(111)
ax.axis('off')
length = 7
colLabels = ['Stage %s' %i for i in range(1,length+1)] # <--- 1 row, 7 columns
cellText = np.random.randint(0, 10, (1,length))
tab = ax.table(cellText=cellText, colLabels=colLabels, bbox=[0, 0, 1, 1], cellLoc = 'center')
tab.auto_set_font_size(False)
tab.set_fontsize(14)
Table with multiple rows
cellText = np.random.randint(0, 10, (3,length)) # <--- 3 rows, 7 columns
tab = ax.table(cellText=cellText, colLabels=colLabels, bbox=[0, 0, 1, 1], cellLoc = 'center')
To get a single row with multiple columns starting from 2 rows, 7 columns
tab = ax.table(cellText=[['']*length], colLabels=colLabels, bbox=[0, 0, 1, 1], cellLoc = 'center')
cells=tab.get_celld()
for i in range(length):
cells[(1,i)].set_height(0)
Getting a single column Using in the above code
length = 1
produces
A table expects two dimensional cellText. I.e. the mth column of the nth row has the content cellText[n][m]. If cellText=['Stage 1'], cellText[0][0] will evaluate to "S", because there is one row and the string inside is indexed as the columns. Instead you probably want to use
ax.table(cellText=[['Stage 1']])
i.e. the whole text as the first column of the first row.
Now the underlying question seems to be how to add a section title, and maybe using a table for that is not the best approach? At least a similar result could be achieved with a usual text,
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 2))
ax.tick_params(labelleft=False, left=False, labelbottom=False, bottom=False)
ax.annotate('Stage 1', (.5,.5), ha="center", va="center", fontsize=24)
plt.show()
I may be misunderstanding your question, but if your ultimate goal is to group multiple plots together in PDF, one solution is to make each of your plots a subplot of the same figure. For example:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import random
# Declare the PDF file and the single figure
pp = PdfPages('test.pdf')
thefig = plt.figure()
thefig.suptitle("Group 1")
# Generate 4 subplots for the same figure, arranged in a 2x2 grid
subplots = [ ["Plot One", 221], ["Plot Two", 222],
["Plot Three", 223], ["Plot Four", 224] ]
for [subplot_title, grid_position] in subplots:
plt.subplot(grid_position)
plt.title(subplot_title)
# Make a random bar graph:
plt.bar(range(1,11), [ random.random() for i in range(10) ])
# Add some spacing, so that the writing doesn't overlap
plt.subplots_adjust(hspace=0.35, wspace=0.35)
# Finish
pp.savefig()
pp.close()
When I do this, I get something like the following:

Matplotlib create table data for one row multiple columns

I'm working with a dictionary of values which have a string (date) and float for time in milliseconds. I want to present the data in a bar graph and also with a table below. I have the bar graph working but the table gets messed up. I want the dates as columns and time as a single row.
The dictionary is something like:
time_and_dates_for_plot = {'04-26': 488.1063166666667, '04-27': 289.7289333333333, '04-28': 597.2343999999999, '04-29': 0, '04-30': 0, '05-01': 1061.958075}
plot.bar(range(len(time_and_dates_for_plot)), time_and_dates_for_plot.values(), align='center')
plot.xticks(range(len(time_and_dates_for_plot)), list(time_and_dates_for_plot.keys()))
plot.xlabel('Date (s)')
plot.ylabel('milliseconds')
plot.grid(True)
plot.gca().set_position((.1, .3, .8, .6))
col_labels = list(time_and_dates_for_plot.keys())
print(col_labels)
row_labels = ['ms']
cell_text = []
val = []
for key in time_and_dates_for_plot.keys():
val.append((time_and_dates_for_plot.get(key)))
cell_text.append(val)
val = []
print(cell_text)
plot.table(cellText=cell_text, colLabels=col_labels)
plot.show()
As you can see from the picture, I get all entries under one column where as I want something like one cell data under one coloumn (just tabulate plot data).
Also, how do I add some padding between the table and graph?
First time I'm using matplotlib and pretty sure I'm missing something. Any help is really appreciated.
In the table function you need an extra pair of brackets []. ...cellText=[cell_text]...
Also, you can use subplots to have a better arrangement of the plots. Here, my solution uses subplots of 2 rows withheight_ratiosof 8 to 1, and ahspace` pf 0.3
import matplotlib as mpl
import matplotlib.pyplot as plt
time_and_dates_for_plot = {'04-26': 488.1063166666667,
'04-27': 289.7289333333333,
'04-28': 597.2343999999999,
'04-29': 0,
'04-30': 0,
'05-01': 1061.958075}
fig,axs = plt.subplots(figsize=(8,5),ncols=1,nrows=2,
gridspec_kw={'height_ratios':[8,1],'hspace':0.3})
ax = axs[0]
ax.bar(range(len(time_and_dates_for_plot)),
time_and_dates_for_plot.values(), align='center')
ax.set_xticks(range(len(time_and_dates_for_plot)),
list(time_and_dates_for_plot.keys()))
ax.set_xlabel('Date (s)')
ax.set_ylabel('milliseconds')
ax.grid(True)
col_labels = list(time_and_dates_for_plot.keys())
row_labels = ['ms']
cell_text = []
for key in time_and_dates_for_plot.keys():
cell_text += [time_and_dates_for_plot[key]]
ax = axs[1]
ax.set_frame_on(False) # turn off frame for the table subplot
ax.set_xticks([]) # turn off x ticks for the table subplot
ax.set_yticks([]) # turn off y ticks for the table subplot
ax.table(cellText=[cell_text], colLabels=col_labels, loc='upper center')
plt.show()
The output looks like:
** UPDATE **
Using only one subplot, no xticklabels, sorted dates, nicer numbers with %g, and larger table cells using bbox :
import matplotlib as mpl
import matplotlib.pyplot as plt
time_and_dates_for_plot = {'04-26': 488.1063166666667,
'04-27': 289.7289333333333,
'04-28': 597.2343999999999,
'04-29': 0,
'04-30': 0,
'05-01': 1061.958075}
N = len(time_and_dates_for_plot)
colLabels = sorted(time_and_dates_for_plot.keys())
fig,ax = plt.subplots()
aa = ax.bar(range(N),[time_and_dates_for_plot[x] for x in colLabels],
align='center')
ax.set_xlabel('Date')
ax.set_ylabel('milliseconds')
ax.set_xticklabels([]) # turn off x ticks
ax.grid(True)
fig.subplots_adjust(bottom=0.25) # making some room for the table
cell_text = []
for key in colLabels:
cell_text += ["%g"%time_and_dates_for_plot[key]]
ax.table(cellText=[cell_text], colLabels=colLabels,
rowLabels=['ms'],cellLoc='center',
bbox=[0, -0.27, 1, 0.15])
ax.set_xlim(-0.5,N-0.5) # Helps having bars aligned with table columns
ax.set_title("milliseconds vs Date")
fig.savefig("Bar_graph.png")
plt.show()
Output:
** Update: Making room for the table using subplots_adjust **

Categories