annotate numerics/interpolation.py @ 193:dc495f0b5ee8 default tip

[testing] toxify
author Jeff Hammel <k0scist@gmail.com>
date Sun, 13 Aug 2017 15:52:01 -0700
parents 7dd1b18c9f78
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
1 #!/usr/bin/env python
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
2
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
3 """
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
4 interpolation
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
5 """
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
6
66
7dd1b18c9f78 minor cleanup and better error
Jeff Hammel <k0scist@gmail.com>
parents: 37
diff changeset
7 # imports
36
285a886255e4 missing import
Jeff Hammel <k0scist@gmail.com>
parents: 10
diff changeset
8 import argparse
37
2891db98a9b7 missing import
Jeff Hammel <k0scist@gmail.com>
parents: 36
diff changeset
9 import sys
36
285a886255e4 missing import
Jeff Hammel <k0scist@gmail.com>
parents: 10
diff changeset
10
10
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
11 __all__ = ['neighbors', 'linear_interpolation', 'InterpolateParser']
4
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
12
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
13 def neighbors(start, finish):
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
14 """
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
15 returns the neighbors in finish from start
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
16 assumes both are sorted
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
17 """
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
18 assert finish
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
19 index = 0
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
20 retval = []
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
21 for x in start:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
22 # endpoints
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
23 if x < finish[0]:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
24 retval.append((None, 0))
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
25 continue
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
26 if x > finish[-1]:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
27 retval.append((len(finish)-1, None))
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
28 continue
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
29 # traverse
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
30 try:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
31 while True:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
32 if x < finish[index] or x > finish[index+1]:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
33 index += 1
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
34 continue
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
35 else:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
36 break
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
37 retval.append((index, index+1))
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
38 except IndexError:
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
39 retval.append((len(finish)-2, len(finish)-1))
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
40
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
41 return retval
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
42
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
43
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
44 def linear_interpolation(data, points):
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
45 """
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
46 linearly interpolate data to points
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
47
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
48 data -- iterable of 2-tuples (or equivalent) of `x,y`
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
49 points -- `x`-values to interpolate to
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
50 """
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
51
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
52 # ensure we are sorted
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
53 data = sorted(data, key=lambda x: x[0])
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
54 points = sorted(points)
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
55
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
56 # get the neighbors
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
57 x = [value[0] for value in data]
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
58 nearest_neighbors = neighbors(points, x)
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
59
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
60 # we don't support endpoints yet; this is interpolation, not extrapolation
66
7dd1b18c9f78 minor cleanup and better error
Jeff Hammel <k0scist@gmail.com>
parents: 37
diff changeset
61 if any([(neighbor[0] is None or neighbor[1] is None)
7dd1b18c9f78 minor cleanup and better error
Jeff Hammel <k0scist@gmail.com>
parents: 37
diff changeset
62 for neighbor in nearest_neighbors]):
7dd1b18c9f78 minor cleanup and better error
Jeff Hammel <k0scist@gmail.com>
parents: 37
diff changeset
63 raise AssertionError("Bad neighbors: {}".format(nearest_neighbors))
4
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
64
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
65 retval = []
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
66 for index, (left, right) in enumerate(nearest_neighbors):
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
67 # linearly interpolate
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
68 ratio = (points[index] - data[left][0])/float(data[right][0] - data[left][0])
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
69 retval.append(ratio*data[right][1] + (1.-ratio)*data[left][1])
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
70 return retval
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
71
10
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
72 class InterpolateParser(argparse.ArgumentParser):
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
73 """CLI option parser"""
4
097296d6132e add interpolation
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
74
10
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
75 def __init__(self, **kwargs):
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
76 kwargs.setdefault('description', __doc__)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
77 argparse.ArgumentParser.__init__(self, **kwargs)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
78 self.add_argument('input', nargs='?',
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
79 type=argparse.FileType('r'), default=sys.stdin,
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
80 help='input file, or read from stdin if ommitted')
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
81 self.add_argument('-o', '--output', dest='output',
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
82 type=argparse.FileType('w'), default=sys.stdout,
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
83 help="output file, or stdout if ommitted")
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
84 self.add_argument('--points', '--print-points', dest='print_points',
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
85 action='store_true', default=False,
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
86 help="print the points to interpolate to and exit")
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
87 self.options = None
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
88
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
89 def parse_args(self, *args, **kw):
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
90 options = argparse.ArgumentParser.parse_args(self, *args, **kw)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
91 self.validate(options)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
92 self.options = options
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
93 return options
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
94
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
95 def validate(self, options):
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
96 """validate options"""
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
97
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
98 def main(args=sys.argv[1:]):
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
99 """CLI"""
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
100
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
101 # parse command line options
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
102 parser = InterpolateParser()
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
103 options = parser.parse_args(args)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
104
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
105 # read the CSV
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
106 reader = csv.reader(options.input)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
107 data = [[float(col) for col in row] for row in reader]
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
108 ncols = set([len(row) for row in data])
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
109 assert len(ncols) == 1
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
110 ncols = ncols.pop()
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
111 assert ncols > 1
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
112
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
113 # get `x` values
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
114 data = sorted(data, key=lambda x: x[0])
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
115 x = [row[0] for row in data]
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
116 xmin = int(x[0]) + 1
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
117 xmax = int(x[-1])
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
118 points = range(xmin, xmax+1)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
119 if options.print_points:
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
120 print ('\n'.join([str(point) for point in points]))
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
121 return
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
122
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
123 # make into x,y series
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
124 series = [[(row[0], row[col]) for row in data]
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
125 for col in range(1,ncols)]
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
126
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
127 # interpolate
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
128 interpolated = [linear_interpolation(s, points) for s in series]
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
129
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
130 # output interpolated data
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
131 writer = csv.writer(options.output)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
132 for row in zip(points, *interpolated):
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
133 writer.writerow(row)
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
134
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
135 if __name__ == '__main__':
Jeff Hammel <k0scist@gmail.com>
parents: 4
diff changeset
136 main()