import axi import csv import math MIN_SIZE = 0.01 MAX_SIZE = 0.8 def circle(cx, cy, r, n): points = [] for i in range(n + 1): a = 2 * math.pi * i / n x = cx + math.cos(a) * r y = cy + math.sin(a) * r points.append((x, y)) return points def fill_circle(cx, cy, cr, n): result = [] r = cr while r > 0: result.append(circle(cx, cy, r, n)) r -= 0.05 result[-1].append((cx, cy)) return result def punchcard_from_csv(csv_path): with open(csv_path, 'rb') as fp: reader = csv.reader(fp) csv_rows = list(reader) row_labels = [x[0] for x in csv_rows[1:]] col_labels = csv_rows[0][1:] data = [] for csv_row in csv_rows[1:]: row = [] for value in csv_row[1:]: try: value = float(value) except ValueError: value = None row.append(value) data.append(row) lo = min(x for row in data for x in row if x) hi = max(x for row in data for x in row if x) min_area = math.pi * (MIN_SIZE / 2.0) ** 2 max_area = math.pi * (MAX_SIZE / 2.0) ** 2 paths = [] for r, row in enumerate(data): for c, value in enumerate(row): if not value: continue pct = 1.0 * (value - lo) / (hi - lo) pct = pct ** 0.5 area = pct * (max_area - min_area) + min_area radius = (area / math.pi) ** 0.5 paths.extend(fill_circle(c, r, radius, 90)) for r, label in enumerate(row_labels): d = axi.Drawing(axi.text(label.upper(), axi.TIMESR)) d = d.scale(0.02, 0.02).move(-1, r, 0.5, 0.5) paths.extend(d.paths) for c, label in enumerate(col_labels): d = axi.Drawing(axi.text(label.upper(), axi.TIMESR)) d = d.scale(0.02, 0.02).move(c, -1, 0.5, 0.5) paths.extend(d.paths) d = axi.Drawing(paths) d = d.scale_to_fit(12, 8.5) print 'joining paths' d = d.join_paths(0.02) print 'simplifying paths' d = d.simplify_paths(0.001) d.render().write_to_png('out.png') axi.draw(d) if __name__ == '__main__': import sys punchcard_from_csv(sys.argv[1])