Axis text orientation on openpyxl chart - python

I'm generating a ScatterChart with pyopenxl from a pandas dataframe.
I am trying to change the rotation of the text in the X axis to 270º but I cannot found documentation about how to do it.
This is the code to generate the chart.
import numpy as np
from openpyxl.chart import ScatterChart, Reference, Series
from openpyxl.chart.axis import DateAxis
import pandas as pd
def generate_chart_proyeccion(writer_sheet, col_to_graph, start_row, end_row, title):
"""
Construct a new chart object
:param writer_sheet: Worksheet were is data located
:param col_to_graph: Column of data to be plotted
:param start_row: Row where data starts
:param end_row: Row where data ends
:param title: Chart title
:return: returns a chart object
"""
chart = ScatterChart()
chart.title = title
chart.x_axis.number_format = 'd-mmm HH:MM'
chart.x_axis.majorTimeUnit = "days"
chart.x_axis.title = "Date"
chart.y_axis.title = "Value"
chart.legend.position = "b"
data = Reference(writer_sheet, min_col=col_to_graph, max_col=col_to_graph, min_row=start_row, max_row=end_row)
data_dates = Reference(writer_sheet, min_col=1, max_col=1, min_row=start_row, max_row=end_row) # Corresponde a la columna con la fecha
serie = Series(data, data_dates, title_from_data=True)
chart.series.append(serie)
return chart
# Write data to excel
writer = pd.ExcelWriter("file.xlsx", engine='openpyxl')
df = pd.DataFrame(np.random.randn(10,1), columns=['Value'], index=pd.date_range('20130101',periods=10,freq='T'))
start_row = 1 # Row to start writing df in excel
df.to_excel(writer, sheet_name="Sheet1", startrow=start_row)
end_row = start_row + len(df) # End of the data
chart = generate_chart_proyeccion(writer.sheets["Sheet1"], 2, start_row, end_row, "A title")
# Añado gráfico a excel
writer.sheets["Sheet1"].add_chart(chart, "C2")
writer.save()
This is the output chart that I got.
This is the output chart that I want.
Thanks!

This is unfortunately nothing like as simple as it should be because in the specification this is one of the areas where the schema changes from SpreadsheetDrawingML to DrawingML, which is far more abstract. The best thing to do is actually create two sample files and compare them. In this case this difference is in rot or rotation attribute of the txPr or textProperties of the axis. This is covered in § 21.1.2.1.1 of the OOXML specification.
The following code should work, but might require you to create a TextProperties object:
chart.x_axis.textProperties.bodyProperties.rot = -5400000

I had the same question - this SO post by #oldhumble solved it for me - please see Rotate the axis of an excel chart using openpyxl

Related

Seeking to modify code to pull data from an excel sheet where column A has X numeric value. ( ie all rows with value= 0 )

Just to be upfront, I am a Mechanical Engineer with limited coding experience thou I have some programming classes under my belt( Java, C++, and lisp)
I have inherited this code from my predecessor and am just trying to make it work for what I'm doing with it. I need to iterate through an excel file that has column A values of 0, 1, 2, and 3 (in the code below this correlates to "Revs" ) but I need to pick out all the value = 0 and put into a separate folder, and again for value = 2, etc.. Thank you for bearing with me, I appreciate any help I can get
import pandas as pd
import numpy as np
import os
import os.path
import xlsxwriter
import matplotlib.pyplot as plt
import six
import matplotlib.backends.backend_pdf
from matplotlib.gridspec import GridSpec
from matplotlib.ticker import AutoMinorLocator, MultipleLocator
def CamAnalyzer(entryName):
#Enter excel data from file as a dataframe
df = pd.read_excel (str(file_loc) + str(entryName), header = 1) #header 1 to get correct header row
print (df)
#setup grid for plots
plt.style.use('fivethirtyeight')
fig = plt.figure(figsize=(17,22))
gs = GridSpec(3,2, figure=fig)
props = dict(boxstyle='round', facecolor='w', alpha=1)
#create a list of 4 smaller dataframes by splitting df when the rev count changes and name them
dfSplit = list(df.groupby("Revs"))
names = ["Air Vent","Inlet","Diaphram","Outlet"]
for x, y in enumerate(dfSplit):
#for each smaller dataframe #x,(df-y), create a polar plot and assign it to a space in the grid
dfs = y[1]
r = dfs["Measurement"].str.strip(" in") #radius measurement column has units. ditch em
r = r.apply(pd.to_numeric) + zero/2 #convert all values in the frame to a float
theta = dfs["Rads"]
if x<2:
ax = fig.add_subplot(gs[1,x],polar = True)
else:
ax = fig.add_subplot(gs[2,x-2],polar = True)
ax.set_rlim(0,0.1) #set limits to radial axis
ax.plot(theta, r)
ax.grid(True)
ax.set_title(names[x]) #nametag
#create another subplot in the grid that overlays all 4 smaller dataframes on one plot
ax2 = fig.add_subplot(gs[0,:],polar = True)
ax2.set_rlim(0,0.1)
for x, y in enumerate(dfSplit):
dfs = y[1]
r = dfs["Measurement"].str.strip(" in")
r = r.apply(pd.to_numeric) + zero/2
theta = dfs["Rads"]
ax2.plot(theta, r)
ax2.set_title("Sample " + str(entryName).strip(".xlsx") + " Overlayed")
ax2.legend(names,bbox_to_anchor=(1.1, 1.05)) #place legend outside of plot area
plt.savefig(str(file_loc) + "/Results/" + str(entryName).strip(".xlsx") + ".png")
print("Results Saved")
I'm on my phone, so I can't check exact code examples, but this should get you started.
First, most of the code you posted is about graphing, and therefore not useful for your needs. The basic approach: use pandas (a library), to read in the Excel sheet, use the pandas function 'groupby' to split that sheet by 'Revs', then iterate through each Rev, and use pandas again to write back to a file. Copying the relevant sections from above:
#this brings in the necessary library
import pandas as pd
#Read excel data from file as a dataframe
#header should point to the row that describes your columns. The first row is row 0.
df = pd.read_excel("filename.xlsx", header = 1)
#create a list of 4 smaller dataframes using GroupBy.
#This returns a 'GroupBy' object.
dfSplit = df.groupby("Revs")
#iterate through the groupby object, saving each
#iterating over key (name) and value (dataframes)
#use the name to build a filename
for name, frame in dfSplit:
frame.to_excel("Rev "+str(name)+".xlsx")
Edit: I had a chance to test this code, and it should now work. This will depend a little on your actual file (eg, which row is your header row).

Choropleth map is not showing color variation in the output

I am not getting any color variation in my output map even after the choropleth is linked with the geo_data and the data frame is linked with data parameter in the choropleth method.
I have provided the "key_on" parameter rightly and the "columns" parameter rightly. I have removed all the NULL values from the Dataframe.
import pandas as pd
from pandas import read_csv
import folium
import os
import webbrowser
crimes = read_csv('Dataframe.csv',error_bad_lines=False)
vis = os.path.join('Community_Areas.geojson')
m = folium.Map(location = [41.878113, -87.629799], zoom_start = 10, tiles = "cartodbpositron")
m.choropleth(geo_data=vis, data = crimes, columns = ['Community Area', 'count'], fill_color = 'YlGn', key_on = 'feature.properties.area_numbe')
folium.LayerControl().add_to(m)
m.save('map.html')
webbrowser.open(filepath)
I expected a colored choropleth map, but the actual output was completely grey. I will add the code, data, output in the link below.
Code link: https://github.com/rahul0070/Stackoverflow_question_data
Data
,Community Area,count
0,25.0,92679
1,8.0,48751
2,43.0,47731
3,23.0,45943
4,29.0,44819
5,28.0,42243
6,71.0,40624
7,67.0,40157
8,24.0,39680
9,32.0,38513
10,49.0,37227
11,68.0,37023
12,69.0,35874
13,66.0,33877
14,44.0,32256
15,6.0,31043
16,26.0,30565
17,27.0,28113
18,61.0,27362
19,22.0,27329
20,46.0,26897
21,19.0,26198
22,30.0,24362
23,53.0,22645
24,42.0,21284
25,7.0,21047
26,1.0,19932
27,3.0,19799
28,15.0,17820
29,38.0,17660
30,2.0,17213
31,73.0,17071
32,16.0,15926
33,40.0,14943
34,58.0,14143
35,31.0,13934
36,63.0,13203
37,70.0,12980
38,35.0,12965
39,14.0,12714
40,77.0,12612
41,21.0,12587
42,75.0,11156
43,65.0,10812
44,51.0,10348
45,56.0,10176
46,4.0,9984
47,33.0,8987
48,60.0,8982
49,76.0,8938
50,20.0,8922
51,17.0,8536
52,41.0,8076
53,48.0,7823
54,5.0,7761
55,45.0,7485
56,39.0,7466
57,52.0,7150
58,54.0,6777
59,10.0,6372
60,11.0,6072
61,34.0,6053
62,62.0,5736
63,50.0,5730
64,59.0,5695
65,57.0,5244
66,64.0,5194
67,72.0,4962
68,37.0,4908
69,13.0,4570
70,36.0,3359
71,74.0,3145
72,55.0,3109
73,18.0,3109
74,12.0,2454
75,47.0,2144
76,9.0,1386
After reading the data what I found was you did not convert the data type of Community Area column to string type as the geojson file contains the key in the form of string. So the data type of both the key and column should match.
Here is a full fledged solution to your answer
# importing libraries
import pandas as pd
from pandas import read_csv
import folium
import os
import webbrowser
# read the data
crimes = read_csv('Dataframe.csv',error_bad_lines=False)
# convert float to int then to string
crimes['Community Area'] = crimes['Community Area'].astype('int').astype('str')
# choropleth map
vis = 'Community_Areas.geojson'
m = folium.Map(location = [41.878113, -87.629799], zoom_start = 10, tiles = "cartodbpositron")
m.choropleth(geo_data=vis, data = crimes, columns = ['Community Area', 'count'], fill_color = 'YlGn', key_on = 'feature.properties.area_num_1')
folium.LayerControl().add_to(m)
m.save('map.html')
webbrowser.open('map.html')
Please comment if you find difficulty in understanding any part of code. For the key_on parameter you can also try feature.properties.area_numbe

How to order the columns in a pandas data frame non-alphabetically and to merge the cells of a matplotlib table?

I am trying to plot a table in python. I have it working and such...but when I plot my table, it doesn't plot the Pass/Fail column at the end how I have it written. It seems that the columns are being displayed in alphabetical order.
How do I disable this.
I want to add the last column but just as one row. Basically a big check box but when I do that it gives me an error that the arrays must be the same length, which makes sense...but how can I go around this and just have one large column with no rows..?
import pandas as pd
import matplotlib.pyplot as plt
MinP_M=5
Min_M=6
Per_M=7
Per_G=8
Per2_M=9
PerFlat_M=10
MaxPL_M=11
Max_M=12
GF_M =13
fig1 = plt.figure()
fig1.set_size_inches(8.7,11.75,forward=True)
ax1=fig1.add_subplot(111)
ax1.axis('off')
ax1.axis('tight')
data2={'Min':['%s'%MinP_M,'%s'%Min_M,'',''],
'Typ':['%s'%Per_M,'%s'%Per_G,'%s'%Per2_M,'+/- %s'%PerFlat_M],
'Max':['%s'%MaxPL_M,'','%s'%Max_M,'+/- %s'%GF_M],
'Pass/Fail':['','','','']
}
df2 = pd.DataFrame(data2)
the_table2=ax1.table(cellText=df2.values,colWidths=[0.15]*5,rowLabels=['A','B','C', 'D'],colLabels=df2.columns,loc='center')
plt.show()
The first part is relatively easy to solve. As you create your pandas data frame using a dict, the order of keywords and thus the order of columns is not fixed. To get the ordering correct, use the columns keyword. The second part was a bit more tricky. The solution I found here is to overlay your original table with a second table and then adding another cell to that second table that has the same height as the four cells of the original table. For that you have to first obtain the cell dictionary from the table instance and sum up the heights of the table rows. Please see the code below:
import pandas as pd
import matplotlib.pyplot as plt
MinP_M=5
Min_M=6
Per_M=7
Per_G=8
Per2_M=9
PerFlat_M=10
MaxPL_M=11
Max_M=12
GF_M =13
fig1 = plt.figure()
##this line entirely messed up the plot for me (on Mac):
##fig1.set_size_inches(8.7,11.75,forward=True)
ax1=fig1.add_subplot(111)
ax1.axis('off')
ax1.axis('tight')
data2={'Min':['%s'%MinP_M,'%s'%Min_M,'',''],
'Typ':['%s'%Per_M,'%s'%Per_G,'%s'%Per2_M,'+/- %s'%PerFlat_M],
'Max':['%s'%MaxPL_M,'','%s'%Max_M,'+/- %s'%GF_M],
'Pass/Fail':['','','','']
}
##fix the column ordering with a list:
keys = ['Min', 'Typ', 'Max', 'Pass/Fail']
df2 = pd.DataFrame(data2, columns=keys)
##defining the size of the table cells
row_label_width = 0.05
col_width = 0.15
col_height = 0.05
the_table2=ax1.table(
cellText=df2.values,
colWidths=[col_width]*4,
rowLabels=['A','B','C', 'D'],
colLabels=df2.columns,
##loc='center', ##this has no effect if the bbox keyword is used
bbox = [0,0,col_width*4,col_height*5],
)
celld = the_table2.get_celld()
##getting the heights of the header and the columns:
row_height_tot = 0
for (i,j),cell in celld.items():
if j==3 and i>0: #last column, but not the header
row_height_tot += cell.get_height()
the_table3=ax1.table(
cellText=['0'], ##cannot be empty
colLabels=df2.columns[-1:],
colWidths=[col_width],
bbox = [col_width*3,0,col_width,col_height*5],
)
the_table3.add_cell(1,0,col_width,row_height_tot)
fig1.tight_layout()
plt.show()
I had to turn off some of your formatting options as they gave weird results on my computer. If you want to have the table centred, play with the bbox options in the table commands. The final result looks like this:
Hope this helps.

Rotate the axis of an excel chart using openpyxl

there
I'm trying to using openpyxl to deal with Excel Data.Drawing the picture out and exporting them is OK.But the x_axis is not pretty enough,I want to rotate that but haven't found the solution in the doc.
Here is the solution using XlsxWriter:solution.
My Code is something like this:
from openpyxl import load_workbook
from openpyxl.chart import (
ScatterChart,
LineChart,
Reference,
Series,
shapes,
text,
axis)
wb = load_workbook('text.xlsx')
ws = wb.active
c5 = ScatterChart()
x = Reference(ws, min_col=1, min_row=2, max_row=ws.max_row)
for i in range(3,5):
values = Reference(ws, min_col=i, min_row=1, max_row=ws.max_row)
series = Series(values, xvalues=x, title_from_data=True)
# series.marker.symbol = 'triangle'
c5.series.append(series)
c5.x_axis.number_format='yyyy/mm/dd'
c5.x_axis.title = 'Date'
Thanks all!
My Python version is 3.5.2 and openpyxl is 2.4.0
----------------newcode to rotate but make file broken and need repaired
from openpyxl.chart.text import RichText
from openpyxl.drawing.text import RichTextProperties
c5.x_axis.txPr = RichText(bodyPr=RichTextProperties(rot="-2700000"))
------------- code in Excel xml
<valAx>
some codes here
<txPr>
<a:bodyPr rot="-1350000" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"/>
</txPr>
<crossAx val="20"/>
</valAx>
codes above breaks the file until I add these in it
<txPr>
<a:bodyPr rot="-1350000" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"/>
<a:p xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:pPr xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:defRPr xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"/>
</a:pPr>
<a:endParaRPr xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" lang="zh-CN"/>
</a:p>
</txPr>
Thanks to #Charlie Clark
I finally get the answer
The adding codes are below:
from openpyxl.chart.text import RichText
from openpyxl.drawing.text import RichTextProperties,Paragraph,ParagraphProperties, CharacterProperties
c5.x_axis.txPr = RichText(bodyPr=RichTextProperties(anchor="ctr",anchorCtr="1",rot="-2700000",
spcFirstLastPara="1",vertOverflow="ellipsis",wrap="square"),
p=[Paragraph(pPr=ParagraphProperties(defRPr=CharacterProperties()), endParaRPr=CharacterProperties())])
It is a little complicated ,But can be learned from openpyxl documentation
txPr is typed RichText,and it consists of (bodyPr and p),bodyPr defines it's properties, p is a sequence and decides if the axis will be shown or not.
it can rotate the x_axis of the chart -45 degrees.
It also might be a bit more convenient to make a copy of an existing property and set its rotation:
chart.x_axis.title = 'Date'
chart.x_axis.txPr = deepcopy(chart.x_axis.title.text.rich)
chart.x_axis.txPr.properties.rot = "-2700000"

Swap X and Y Axis in xlsxwriter

I just starting with XlsxWriter (0.8.4). I am trying to create charts but the x and y axis are not correct, I want to swap them, x for y.
I'm using these code blocks to create the sheet and the chartsheet
def new_sheet(self,sheetnm,sheetdata):
self.ws = self.wb.add_worksheet(sheetnm)
logging.info(self.ws)
metadata = sheetdata[1]
head = (colh[0] for colh in metadata)
self.ws.write_row(0,0,head)
rows = sheetdata[0]
for ix,row in enumerate(rows):
self.ws.write_row(ix+1,0,row)
def new_chart(self,sheetnm,ctitle,xtitle,ytitle,rows,cols):
self.cs = self.wb.add_chartsheet(sheetnm+"_chart")
chart = self.wb.add_chart({'type': 'bar'})
chart.set_title({'name': ctitle})
for row in range(1,rows):
chart.add_series({'categories':[sheetnm,0,1,0,cols-1],'values':[sheetnm,row,1,row,cols-1],'name':[sheetnm,row,0,row,0]})
chart.set_x_axis({'name': xtitle})
chart.set_y_axis({'name': ytitle})
self.cs.set_chart(chart)
It is working, making the sheets. If I just use Excel to insert a chart, it inserts it with the expect x/y axis. How can I do the same?
[
That worked great. Thanks! I'm not used to doing charts so the different types confuse me, column vs. vertical bar vs. horizontal bar.

Categories

Resources