From 8fa7f2451bbfb2fb0323bbd49abd4d714c40b039 Mon Sep 17 00:00:00 2001 From: Recolic Keghart <root@recolic.net> Date: Mon, 18 Dec 2017 19:56:19 +0800 Subject: [PATCH] New function added: Generate Multi Lines on single figure. --- TuningFork/draw.py | 8 ++-- TuningFork/others_data/tmp1.dat | 41 +++++++++++++++++ TuningFork/others_data/tmp2.dat | 30 +++++++++++++ deprecated.py | 78 +++++++++++++++++++++++++++++++++ quickmap.py | 78 ++++++++++++++++++++++----------- 5 files changed, 206 insertions(+), 29 deletions(-) create mode 100644 TuningFork/others_data/tmp1.dat create mode 100644 TuningFork/others_data/tmp2.dat create mode 100644 deprecated.py diff --git a/TuningFork/draw.py b/TuningFork/draw.py index ae325a8..a1d1f90 100755 --- a/TuningFork/draw.py +++ b/TuningFork/draw.py @@ -3,12 +3,12 @@ import sys sys.path.append('..') import quickmap +from quickmap import GetMultiMap, GetLine -x,y = quickmap.DataFileToXYArray('without_damping.dat') -quickmap.GetMap(x,y,smoothLine=True) +x1,y1 = quickmap.DataFileToXYArray('without_damping.dat') +x2,y2 = quickmap.DataFileToXYArray('with_damping.dat') -x,y = quickmap.DataFileToXYArray('with_damping.dat') -quickmap.GetMap(x,y,smoothLine=True) +GetMultiMap(GetLine(x1, y1, smoothLine=True) + GetLine(x2, y2, smoothLine=True)) sq_T = '14.633 19.327 24.256 29.192 34.589 39.547'.split(' ') sq_T = [float(e) for e in sq_T] diff --git a/TuningFork/others_data/tmp1.dat b/TuningFork/others_data/tmp1.dat new file mode 100644 index 0000000..3d8c92b --- /dev/null +++ b/TuningFork/others_data/tmp1.dat @@ -0,0 +1,41 @@ +# f(Hz) u + +260.048 0.059 +260.348 0.068 +260.648 0.081 +260.948 0.102 +261.248 0.136 +261.448 0.178 +261.648 0.260 +261.748 0.341 +261.798 0.403 +261.848 0.491 +261.898 0.629 +261.928 0.744 +261.948 0.863 +261.958 0.920 +261.968 0.999 +261.978 1.060 +261.988 1.154 +261.998 1.226 +262.008 1.321 +262.018 1.404 +262.028 1.452 +262.038 1.492 +262.048 1.500 +262.058 1.482 +262.068 1.434 +262.078 1.382 +262.088 1.306 +262.098 1.244 +262.118 1.103 +262.128 1.027 +262.228 0.570 +262.328 0.376 +262.428 0.277 +262.738 0.187 +263.548 0.075 +263.798 0.059 +264.048 0.043 + + diff --git a/TuningFork/others_data/tmp2.dat b/TuningFork/others_data/tmp2.dat new file mode 100644 index 0000000..2b90708 --- /dev/null +++ b/TuningFork/others_data/tmp2.dat @@ -0,0 +1,30 @@ +259.774 0.057 +260.074 0.065 +260.374 0.078 +260.674 0.097 +260.974 0.130 +261.274 0.201 +261.474 0.323 +261.574 0.464 +261.625 0.589 +261.675 0.793 +261.705 0.961 +261.715 1.034 +261.725 1.112 +261.735 1.167 +261.744 1.218 +261.754 1.267 +261.764 1.298 +261.774 1.305 +261.784 1.290 +261.794 1.268 +261.804 1.220 +261.814 1.179 +262.114 0.293 +262.314 0.182 +262.514 0.129 +262.814 0.089 +263.214 0.061 +263.414 0.052 +263.714 0.042 +263.774 0.041 diff --git a/deprecated.py b/deprecated.py new file mode 100644 index 0000000..d22f884 --- /dev/null +++ b/deprecated.py @@ -0,0 +1,78 @@ +import functools +import inspect +import warnings + +string_types = (type(b''), type(u'')) + + +def deprecated(reason): + """ + This is a decorator which can be used to mark functions + as deprecated. It will result in a warning being emitted + when the function is used. + """ + + if isinstance(reason, string_types): + + # The @deprecated is used with a 'reason'. + # + # .. code-block:: python + # + # @deprecated("please, use another function") + # def old_function(x, y): + # pass + + def decorator(func1): + + if inspect.isclass(func1): + fmt1 = "Call to deprecated class {name} ({reason})." + else: + fmt1 = "Call to deprecated function {name} ({reason})." + + @functools.wraps(func1) + def new_func1(*args, **kwargs): + warnings.simplefilter('always', DeprecationWarning) + warnings.warn( + fmt1.format(name=func1.__name__, reason=reason), + category=DeprecationWarning, + stacklevel=2 + ) + warnings.simplefilter('default', DeprecationWarning) + return func1(*args, **kwargs) + + return new_func1 + + return decorator + + elif inspect.isclass(reason) or inspect.isfunction(reason): + + # The @deprecated is used without any 'reason'. + # + # .. code-block:: python + # + # @deprecated + # def old_function(x, y): + # pass + + func2 = reason + + if inspect.isclass(func2): + fmt2 = "Call to deprecated class {name}." + else: + fmt2 = "Call to deprecated function {name}." + + @functools.wraps(func2) + def new_func2(*args, **kwargs): + warnings.simplefilter('always', DeprecationWarning) + warnings.warn( + fmt2.format(name=func2.__name__), + category=DeprecationWarning, + stacklevel=2 + ) + warnings.simplefilter('default', DeprecationWarning) + return func2(*args, **kwargs) + + return new_func2 + + else: + raise TypeError(repr(type(reason))) \ No newline at end of file diff --git a/quickmap.py b/quickmap.py index b42dc05..748ff0a 100644 --- a/quickmap.py +++ b/quickmap.py @@ -9,6 +9,8 @@ import matplotlib.pyplot as plt from matplotlib import rcParams from scipy.interpolate import spline +from deprecated import deprecated + def dotMultiply(vctA, vctB): if len(vctA) != len(vctB): print("Error: While vcta is ", vctA, " and vctb is ", vctB) @@ -18,16 +20,54 @@ def dotMultiply(vctA, vctB): ans += a * b return ans -def GetMap(arrX, arrY, windowSizeX=12, windowSizeY=8, extendXRate=1, extendYRate=1, polyLine=False, poly_passO=False, poly_maxXPower=1, poly_inverseK=False, smoothLine=False): +def __fetchAnonymousLineName(): + for i in range(10000): + yield 'Unnamed Line ' + str(i) +_fetchAnonymousLineName = __fetchAnonymousLineName() +fetchAnonymousLineName = lambda :next(_fetchAnonymousLineName) + +@deprecated("Use GetMultiMap(GetLine(...)) rather than GetMap() please!") +def GetMap(arrX, arrY, extendXRate=1, extendYRate=1, polyLine=False, poly_passO=False, poly_maxXPower=1, poly_inverseK=False, smoothLine=False): + ''' + A wrapper to GetMultiMap(GetLine(args ...)), just check doc in GetLine(). + ''' + GetMultiMap(GetLine(arrX, arrY, fetchAnonymousLineName(), extendXRate, extendYRate, polyLine, poly_passO, poly_maxXPower, poly_inverseK, smoothLine)) + +def GetMultiMap(lines, windowSizeX=12, windowSizeY=8): + ''' + Usage: GetMultiMap(GetLine([1,2,3], [1,4,9]) + GetLine([2,2.1,2.2], [6,7,8]) + ...) + ''' + colors = ['red', 'orange', 'blue', 'green', 'yellow', 'magenta', 'cyan', 'black'] # You can add more color like: '#123456', '#FFFFAE', etc + def __fetchColor(): + for color in colors: + yield color + raise RuntimeError('Pre-defined colors are used out...... Check quickmap.py::GetMultiMap line 29 to add more color.') + _fetchColor = __fetchColor() + fetchColor = lambda :next(_fetchColor) + + plt.figure(figsize=(windowSizeX, windowSizeY)) + for line in lines: + name, X, Y, px, py = line + plt.scatter(X, Y, color=fetchColor(), label="Sample Point ({})".format(name), linewidth=3) + if len(px) != len(py): + raise ValueError('Invalid line passed to GetMultiMap. Assertion len(px) == len(py) failed because {} != {}.'.format(len(px), len(py))) + if len(px) != 0: + plt.plot(px, py, color=fetchColor(), label="Fitting Line ({})".format(name), linewidth=2) + plt.grid() + plt.show() + + +def GetLine(arrX, arrY, name=fetchAnonymousLineName(), extendXRate=1, extendYRate=1, polyLine=False, poly_passO=False, poly_maxXPower=1, poly_inverseK=False, smoothLine=False): ''' Arguments: - arrX and arrY: array of coordinates of points. Ex: GetMap([1,2,3,4,5], [1,2,3,4,5]) -> y=x + arrX and arrY: array of coordinates of points. Ex: GetLine([1,2,3,4,5], [1,2,3,4,5]) -> y=x + name: Shown in GetMultiMap(). polyLine(conflict with polyLine): Should I draw a fitting line by polynomial fitting? - smoothLine(conflict with smoothLine): Should I draw a fitting line by connecting all points and smooth them? + smoothLine(conflict with smoothLine, **RECOMMENDED**): Should I draw a fitting line by connecting all points and smooth them? passO: Should the fitting line pass (0,0)? That's saying, should k0 be zero? - maxXPower: If I should draw a fitting line, what polynomial function should I use? Ex: GetMap([1,2,3,4,5], [1,4,9,16,25], line=True, maxXPower) -> y=x^2 + maxXPower: If I should draw a fitting line, what polynomial function should I use? Ex: GetLine([1,2,3,4,5], [1,4,9,16,25], line=True, maxXPower) -> y=x^2 inverseK: Usually, I'm drawing the curl `y=KX`, while K=[k0,k1,k2,...], X=[x^0,x^1,x^2,...]. If this switch is set, I'll drawing the curl `KY=x`. Don't worry, this switch is transparent to you. - Ex: GetMap([0,1,1,4,4,9,9], [0,1,-1,2,-2,3,-3], poly_maxXPower=2, polyLine=True, poly_inverseK=True) -> y^2=x + Ex: GetLine([0,1,1,4,4,9,9], [0,1,-1,2,-2,3,-3], poly_maxXPower=2, polyLine=True, poly_inverseK=True) -> y^2=x ReturnValue: void @@ -40,7 +80,7 @@ def GetMap(arrX, arrY, windowSizeX=12, windowSizeY=8, extendXRate=1, extendYRate maxX, maxY = max(arrX) * extendXRate, max(arrY) * extendYRate minX, minY = min(arrX) * extendXRate, min(arrY) * extendYRate X, Y = numpy.array(arrX), numpy.array(arrY) - print('Your input: ', arrX, '|', arrY) + print('Your input for {}: {} | {}'.format(name, arrX, arrY)) if polyLine: print('You want a polynomial fitting, with maxXPower = {}, passO = {}, inverseK = {}.'.format(poly_maxXPower, poly_passO, poly_inverseK)) if smoothLine: @@ -58,20 +98,16 @@ def GetMap(arrX, arrY, windowSizeX=12, windowSizeY=8, extendXRate=1, extendYRate # Fire! if polyLine: + # Generate target line. kInit = [1 for _ in range(poly_maxXPower + 1)] kInit[0] = 0 # guarantee passO. if poly_inverseK: kFinal, _ = leastsq(lossFunc, kInit, args=(Y, X)) - print('Fitting line done. k^-1=', kFinal) + print('Fitting polyLine done. k^-1=', kFinal) else: kFinal, _ = leastsq(lossFunc, kInit, args=(X, Y)) - print('Fitting line done. k=', kFinal) - else: - print('Drawing map without fitting a line...\n') + print('Fitting polyLine done. k=', kFinal) - # Draw function map. - plt.figure(figsize=(windowSizeX, windowSizeY)) - plt.scatter(X, Y, color="red", label="Sample Point", linewidth=3) if polyLine: if poly_inverseK: py = numpy.linspace(minY, maxY, 1000) @@ -79,20 +115,12 @@ def GetMap(arrX, arrY, windowSizeX=12, windowSizeY=8, extendXRate=1, extendYRate else: px = numpy.linspace(minX, maxX, 1000) py = dotMultiply(kFinal, [px ** power for power in range(poly_maxXPower + 1)]) - plt.plot(px, py, color="orange", label="Fitting Line", linewidth=2) - if smoothLine: + elif smoothLine: px = numpy.linspace(minX, maxX, 1000) py = spline(X, Y, px) - plt.plot(px, py, color="orange", label="Fitting Line", linewidth=2) - plt.legend() - plt.grid() - plt.show() - - def toFloat(sstr): - if sstr == '': - return 0.0 - else: - return float(sstr) + else: + px, py = [], [] + return [(name, X, Y, px, py)] #Python list is addible. def _str_remove_extra_space(s): begin, end = 0, len(s) -- GitLab