Build reportlab table without using flowables - python

I want to draw a reportlab table in a pdf. Is there a way to draw the table to specific (x,y) coordinates? I'm not using any flowables. I just want to specify the location of the table.
For drawing charts I use "renderPDF.draw()". This doesn't work for tables.
Thanks!

I found a solution:
canv = Canvas("phello.pdf")
data = [['00', '01', '02', '03', '04'],
['10', '11', '12', '13', '14'],
['20', '21', '22', '23', '24'],
['30', '31', '32', '33', '34'],
['20', '21', '22', '23', '24'],
['20', '21', '22', '23', '24'],
['20', '21', '22', '23', '24'],
['20', '21', '22', '23', '24']]
t = Table(data)
t.wrap(0, 0) # not sure why this is necessary
t.drawOn(canv, 72, 200) # parameters specify the location of the table
canv.save()
regards

If anyone else lands on this question.
I was looking at a way to use Table from platypus and add to a canvas as the TableWidget() wasn't working for me and came up with this code which was re-jigged from:
https://www.blog.pythonlibrary.org/2010/09/21/reportlab-tables-creating-tables-in-pdfs-with-python/
shows a little more clarification
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch, mm
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
c = canvas.Canvas('testing.pdf', pagesize=A4)
c.setFont('Helvetica', 12, leading=None)
def add_table(self):
data= [['00', '01', '02', '03', '04']
, ['10', '11', '12', '13', '14']
, ['20', '21', '22', '23', '24']
, ['30', '31', '32', '33', '34']]
t=Table(data
, 5*[0.4*inch] #width of 5 cols, size of []
, 4*[0.4*inch]) #height of 4 rows, size of []
# below shows which
t.setStyle(TableStyle([('ALIGN' ,(1, 1),(-2,-2),'RIGHT') #instead of -2,-2 you could do 3,2
, ('TEXTCOLOR',(1, 1),(-2,-2),colors.red) #and again here
, ('VALIGN' ,(0, 0),( 0,-1),'TOP')
, ('TEXTCOLOR',(0, 0),( 0,-1),colors.blue)
, ('ALIGN' ,(0,-1),(-1,-1),'CENTER')
, ('VALIGN' ,(0,-1),(-1,-1),'MIDDLE')
, ('TEXTCOLOR',(0,-1),(-1,-1),colors.green)
, ('INNERGRID',(0, 0),(-1,-1), 0.25, colors.black)
, ('BOX' ,(0, 0),(-1,-1), 0.25, colors.black)
]))
t.wrapOn(c, 100*mm, 180*mm) #wrap the table to this width, height in case it spills
t.drawOn(c, 20*mm, 250*mm) #draw it on our pdf at x,y
# for those like me who didn't know, * by inch or mm is because reportlab uses
# something else to render and if you look at an A4 it has digits:
# (595.2755905511812, 841.8897637795277)
add_table(c)
c.showPage()
c.save()
Which produces a plain pdf like this:

Related

filter python list based on values in a different list

I think this is an entry level computer science 101 course question about algorithms and data structures.
I have a list:
VAV_ID_list = ['36','38','21','29','31','25','9','13','14','19','30','8','26','6','34','11','12028','20','27','15','12032','23','16','24','37','39','12033','10']
How I can I filter out these values in VAV_ID_exclude_list from VAV_ID_list?
VAV_ID_exclude_list = ['36','38','21','29','31','25','9','13','14','19','30','8','26','6']
This code below obviously doesnt do anything any tips greatly appreciated.
filtered_VAV_ID_list = [zone for zone in VAV_ID_list if zone == 36]
print(filtered_VAV_ID_list)
This is what you want
list2= [zone for zone in VAV_ID_list if zone not in VAV_ID_exclude_list]
You can do it in multiple ways:
This is the most straightforward way.
>>> [i for i in VAV_ID_list if i not in VAV_ID_exclude_list]
['34', '11', '12028', '20', '27', '15', '12032', '23', '16', '24', '37', '39', '12033', '10']
You can even use sets if the order is not important and you don't have duplicates.
>>> list(set(VAV_ID_list) - set(VAV_ID_exclude_list))
['24', '11', '39', '27', '20', '23', '12033', '12032', '16', '37', '34', '15', '12028', '10']
for el in VAV_ID_list:
if el not in VAV_ID_exclude_list:
print(el)
I think this will do it.

How to automate single line code, with changes in input?

I have created a variable named 'j' which has some values and I want my code to pick one value at a time and execute.
I tried writing code but it does not work. I'm sharing my code please see when it can be improved.
j = ['0', '1', '3', '4', '6', '7', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67']
for i in j:
labels('i') = mne.read_labels_from_annot('sub-CC721377_T1w', parc='aparc', subjects_dir=subjects_dir)['i']
done
According to the MNE documentation, the function read_labels_from_annot returns a list of labels.
Thus, instead of indexing the result with ...)[0] at the end, you should just capture the entire list:
labels = mne.read_labels_from_annot(...)
This would capture a list of labels, rather than a single label, which would have the effect of 'indexing at the end "[0]" from 0 - 67'.
You asked about adding all the results together into a label_all variable. You didn't specify (and I don't know anything about the MNE package), so it's not clear: do the labels ever repeat? Is is possible that "lab123" will occur in every input file? If so, should the label_all store multiple copies of the same value, or just the unique label names?
I think something like this is what you're after:
import mne
def get_labels_for_subject(sub, *, hemi='both', parc='aparc', **kwargs):
"""Get MNE labels for a given subject. **kwargs allows passing named
parameters like subjects_dir, regexp, and others that default to None."""
labels = mne.read_labels_from_annot(sub, hemi=hemi, parc=parc, **kwargs)
return labels
# List of all the subjects
subjects = [
'sub-CC721377_T1w',
'sub-next???',
]
label_all = []
for s in subjects:
label_all.extend(get_labels_for_subject(s, subjects_dir='.'))
print("Got labels:", label_all)

python set manipulation :the simple script does not output right set after unioning or disjoining?

all_tags = ['24', '02', '26', '03', '33', '32', '31', '30', '29', '68', '11']
ref_tag = str('24')
union_tags = set(all_tags) | set(ref_tag)
left_tags = set(all_tags) - set(ref_tag)
print(union_tags)
print(left_tags)
The above is the simple code which I expect elements in union_tags should be the same as those in all_tags. However, the result is
set
(['24', '02', '26', '03', '33', '32', '31', '30', '29', '68', '2', '4', '11'])
The union_tags instead contains two extra elements '2' and '4', which I think it is the result splitting the str '24'.
Again, left_tags should exclude element '24'. However, the result still have the '24'.
Please let me know why. I use the python 2.7 as the interpreter.
Set function accept an iterable with hashable items and convert it to a set object, and since strings are iterables when you pass the string 24 to your set function it converts your string to following set:
{'2', '4'}
And at last the unioin of this set with all_tags would contain items 2 and 4.
If you want to put the 24 in a set as one item you can use {} in order to create your expected set:
>>> ref_tag = {'24'}
set(['24'])

Random data generator mathing a regex in python

In python, I am looking for python code which I can use to create random data matching any regex. For example, if the regex is
\d{1,100}
I want to have a list of random numbers with a random length between 1 and 100 (equally distributed)
There are some 'regex inverters' available (see here) which compute ALL possible matches, which is not what I want, and which is extremely impracticable. The example above, for example, has more then 10^100 possible matches, which never can be stored in a list. I just need a function to return a match by random.
Maybe there is a package already available which can be used to accomplish this? I need a function that creates a matching string for ANY regex, not just the given one or some other, but maybe 100 different regex. I just cannot code them myself, I want the function extract the pattern to return me a matching string.
If the expressions you match do not have any "advanced" features, like look-ahead or look-behind, then you can parse it yourself and build a proper generator
Treat each part of the regex as a function returning something (e.g., between 1 and 100 digits) and glue them together at the top:
import random
from string import digits, uppercase, letters
def joiner(*items):
# actually should return lambda as the other functions
return ''.join(item() for item in items)
def roll(item, n1, n2=None):
n2 = n2 or n1
return lambda: ''.join(item() for _ in xrange(random.randint(n1, n2)))
def rand(collection):
return lambda: random.choice(collection)
# this is a generator for /\d{1,10}:[A-Z]{5}/
print joiner(roll(rand(digits), 1, 10),
rand(':'),
roll(rand(uppercase), 5))
# [A-C]{2}\d{2,20}#\w{10,1000}
print joiner(roll(rand('ABC'), 2),
roll(rand(digits), 2, 20),
rand('#'),
roll(rand(letters), 10, 1000))
Parsing the regex would be another question. So this solution is not universal, but maybe it's sufficient
Two Python libraries can do this: sre-yield and Hypothesis.
sre-yield
sre-yeld will generate all values matching a given regular expression. It uses SRE, Python's default regular expression engine.
For example,
import sre_yield
list(sre_yield.AllStrings('[a-z]oo$'))
['aoo', 'boo', 'coo', 'doo', 'eoo', 'foo', 'goo', 'hoo', 'ioo', 'joo', 'koo', 'loo', 'moo', 'noo', 'ooo', 'poo', 'qoo', 'roo', 'soo', 'too', 'uoo', 'voo', 'woo', 'xoo', 'yoo', 'zoo']
For decimal numbers,
list(sre_yield.AllStrings('\d{1,2}'))
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99']
Hypothesis
The unit test library Hypothesis will generate random matching examples. It is also built using SRE.
import hypothesis
g=hypothesis.strategies.from_regex(r'^[A-Z][a-z]$')
g.example()
with output such as:
'Gssov', 'Lmsud', 'Ixnoy'
For decimal numbers
d=hypothesis.strategies.from_regex(r'^[0-9]{1,2}$')
will output one or two digit decimal numbers: 65, 7, 67 although not evenly distributed. Using \d yielded unprintable strings.
Note: use begin and end anchors to prevent extraneous characters.
From this answer
You could try using python to call this perl module:
https://metacpan.org/module/String::Random

Finding 1st order neighbors using shapefile polygons

I am looking a efficient way to find the 1st order neighbors of a given polygon. My data are in shapefile format.
My first idea was to calculate the x and y coordinates of the polygons' centroids in order to find the neighbor's centroids.
import pysal
from pysal.common import *
import pysal.weights
import numpy as np
from scipy import sparse,float32
import scipy.spatial
import os, gc, operator
def get_points_array_from_shapefile(inFile):
"""
Gets a data array of x and y coordinates from a given shape file
Parameters
----------
shapefile: string name of a shape file including suffix
Returns
-------
points: array (n,2) a data array of x and y coordinates
Notes
-----
If the given shape file includes polygons,
this function returns x and y coordinates of the polygons' centroids
Examples
--------
Point shapefile
>>> from pysal.weights.util import get_points_array_from_shapefile
>>> xy = get_points_array_from_shapefile('../examples/juvenile.shp')
>>> xy[:3]
array([[ 94., 93.],
[ 80., 95.],
[ 79., 90.]])
Polygon shapefile
>>> xy = get_points_array_from_shapefile('../examples/columbus.shp')
>>> xy[:3]
array([[ 8.82721847, 14.36907602],
[ 8.33265837, 14.03162401],
[ 9.01226541, 13.81971908]])
(source: https://code.google.com/p/pysal/source/browse/trunk/pysal/weights/util.py?r=1013)
"""
f = pysal.open(inFile)
shapes = f.read()
if f.type.__name__ == 'Polygon':
data = np.array([shape.centroid for shape in shapes])
elif f.type.__name__ == 'Point':
data = np.array([shape for shape in shapes])
f.close()
return data
inFile = "../examples/myshapefile.shp"
my_centr = get_points_array_from_shapefile(inFile)
This approach could be valid for a regular grid but in my case, I need to find a "more general" solution. The figure shows the problem. Consider the yellow polygon has the referee. The neighbor's polygons are the gray polygons. Using the centroids-neighbors approach, the clear blue polygon is considered a neighbor but it doesn't have a side in common with the yellow polygon.
A recent solution modified from Efficiently finding the 1st order neighbors of 200k polygons can be the following:
from collections import defaultdict
inFile = 'C:\\MultiShapefile.shp'
shp = osgeo.ogr.Open(inFile)
layer = shp.GetLayer()
BlockGroupVertexDictionary = dict()
for index in xrange(layer.GetFeatureCount()):
feature = layer.GetFeature(index)
FID = str(feature.GetFID())
geometry = feature.GetGeometryRef()
pts = geometry.GetGeometryRef(0)
# delete last points because is the first (see shapefile polygon topology)
for p in xrange(pts.GetPointCount()-1):
PointText = str(pts.GetX(p))+str(pts.GetY(p))
# If coordinate is already in dictionary, append this BG's ID
if PointText in BlockGroupVertexDictionary:
BlockGroupVertexDictionary[PointText].append(FID)
# If coordinate is not already in dictionary, create new list with this BG's ID
else:
BlockGroupVertexDictionary[PointText] = [FID]
With this solution, I have a dictionary with vertex coordinates as the keys and a list of block group IDs that have a vertex at that coordinate as the value.
>>> BlockGroupVertexDictionary
{'558324.3057036361423.57178': ['18'],
'558327.4401686361422.40755': ['18', '19'],
'558347.5890836361887.12271': ['1'],
'558362.8645026361662.38757': ['17', '18'],
'558378.7836876361760.98381': ['14', '17'],
'558389.9225016361829.97259': ['14'],
'558390.1235856361830.41498': ['1', '14'],
'558390.1870856361652.96599': ['17', '18', '19'],
'558391.32786361398.67786': ['19', '20'],
'558400.5058556361853.25597': ['1'],
'558417.6037156361748.57558': ['14', '15', '17', '19'],
'558425.0594576362017.45522': ['1', '3'],
'558438.2518686361813.61726': ['14', '15'],
'558453.8892486362065.9571': ['3', '5'],
'558453.9626046361375.4135': ['20', '21'],
'558464.7845966361733.49493': ['15', '16'],
'558474.6171066362100.82867': ['4', '5'],
'558476.3606496361467.63697': ['21'],
'558476.3607186361467.63708': ['26'],
'558483.1668826361727.61931': ['19', '20'],
'558485.4911846361797.12981': ['15', '16'],
'558520.6376956361649.94611': ['25', '26'],
'558525.9186066361981.57914': ['1', '3'],
'558527.5061096362189.80664': ['4'],
'558529.0036896361347.5411': ['21'],
'558529.0037236361347.54108': ['26'],
'558529.8873646362083.17935': ['4', '5'],
'558533.062376362006.9792': ['1', '3'],
'558535.4436256361710.90985': ['9', '16', '20'],
'558535.4437266361710.90991': ['25'],
'558548.7071816361705.956': ['9', '10'],
'558550.2603156361432.56769': ['26'],
'558550.2603226361432.56763': ['21'],
'558559.5872216361771.26884': ['9', '16'],
'558560.3288756362178.39003': ['4', '5'],
'558568.7811926361768.05997': ['1', '9', '10'],
'558572.749956362041.11051': ['3', '5'],
'558573.5437016362012.53546': ['1', '3'],
'558575.3048386362048.77518': ['2', '3'],
'558576.189546362172.87328': ['5'],
'558577.1149386361695.34587': ['7', '10'],
'558579.0999636362020.47297': ['1', '3'],
'558581.6312396362025.36096': ['0', '1'],
'558586.7728956362035.28967': ['0', '3'],
'558589.8015336362043.7987': ['2', '3'],
'558601.3250076361686.30355': ['7'],
'558601.3250736361686.30353': ['25'],
'558613.7793476362164.19871': ['2', '5'],
'558616.4062876361634.7097': ['7'],
'558616.4063116361634.70972': ['25'],
'558618.129066361634.29952': ['7', '11', '22'],
'558618.1290896361634.2995': ['25'],
'558626.9644156361875.47515': ['10', '11'],
'558631.2229836362160.17325': ['2'],
'558632.0261236361600.77448': ['25', '26'],
'558639.495586361898.60961': ['11', '13'],
'558650.4935686361918.91358': ['12', '13'],
'558659.2473416361624.50945': ['8', '11', '22', '24'],
'558664.5218136361857.94836': ['7', '10'],
'558666.4126376361622.80343': ['8', '24'],
'558675.1439056361912.52276': ['12', '13'],
'558686.3385396361985.08892': ['0', '1'],
..................
.................
'558739.4377836361931.57279': ['11', '13'],
'558746.8758486361973.84475': ['11', '13'],
'558751.3440576361902.20399': ['6', '11'],
'558768.8067026361258.4715': ['26'],
'558779.9170276361961.16408': ['6', '11'],
'558785.7399596361571.47416': ['22', '24'],
'558791.5596546361882.09619': ['8', '11'],
'558800.2351726361877.75843': ['6', '8'],
'558802.7700816361332.39227': ['26'],
'558802.770176361332.39218': ['22'],
'558804.7899976361336.78827': ['22'],
'558812.9707376361565.14513': ['23', '24'],
'558833.2667696361940.68932': ['6', '24'],
'558921.2068976361539.98868': ['22', '23'],
'558978.3570116361885.00604': ['23', '24'],
'559022.80716361982.3729': ['23'],
'559096.8905816361239.42141': ['22'],
'559130.7573166361935.80614': ['23'],
'559160.3907086361434.15513': ['22']}
Just incase this is still an open question for the OP or someone else stumbles here.
import pysal as ps
w = ps.queen_from_shapefile('shapefile.shp')
http://pysal.readthedocs.io/en/latest/users/tutorials/weights.html#pysal-spatial-weight-types
I am not familiar with the specific data formats being used, but regardless, think the following idea would work.
In Python you can make sets out of tuples of numbers, i.e. (x,y) and (x1,y1,x2,y2), so it should be possible to make a set representing all the points or edges in a given polygon. Afterwards you would be able use very fast set intersection operations to find all 1st order neighbors.
You might be able to speed the process up using some sort of trivial rejection test to avoid further processing of polygons which could not possibly be a neighbor -- perhaps using your polygons' centroids idea.
Does this explanation make sense?

Categories

Resources