Correções de bugs e pequenas otimizações diversas:
- Otimização do GridPartitioner - Correção na geração de PFLRG's em PFTS - Métodos de __str__ mais intuitivos
This commit is contained in:
parent
dba1919a18
commit
ba1b4fbae6
@ -5,10 +5,11 @@ import matplotlib.colors as pltcolors
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from mpl_toolkits.mplot3d import Axes3D
|
from mpl_toolkits.mplot3d import Axes3D
|
||||||
from sklearn.cross_validation import KFold
|
from sklearn.cross_validation import KFold
|
||||||
import Measures
|
from pyFTS.benchmarks import Measures
|
||||||
from pyFTS.partitioners import Grid
|
from pyFTS.partitioners import Grid
|
||||||
from pyFTS.common import Membership, FuzzySet, FLR, Transformations
|
from pyFTS.common import Membership, FuzzySet, FLR, Transformations
|
||||||
|
|
||||||
|
|
||||||
def getIntervalStatistics(original, models):
|
def getIntervalStatistics(original, models):
|
||||||
ret = "Model & RMSE & MAPE & Sharpness & Resolution & Coverage \\ \n"
|
ret = "Model & RMSE & MAPE & Sharpness & Resolution & Coverage \\ \n"
|
||||||
for fts in models:
|
for fts in models:
|
||||||
@ -21,15 +22,18 @@ def getIntervalStatistics(original,models):
|
|||||||
ret = ret + str(round(Measures.coverage(original[fts.order - 1:], forecasts), 2)) + " \\ \n"
|
ret = ret + str(round(Measures.coverage(original[fts.order - 1:], forecasts), 2)) + " \\ \n"
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def plotDistribution(dist):
|
def plotDistribution(dist):
|
||||||
for k in dist.index:
|
for k in dist.index:
|
||||||
alpha = np.array([dist[x][k] for x in dist]) * 100
|
alpha = np.array([dist[x][k] for x in dist]) * 100
|
||||||
x = [k for x in np.arange(0, len(alpha))]
|
x = [k for x in np.arange(0, len(alpha))]
|
||||||
y = dist.columns
|
y = dist.columns
|
||||||
plt.scatter(x,y,c=alpha,marker='s',linewidths=0,cmap='Oranges',norm=pltcolors.Normalize(vmin=0,vmax=1),vmin=0,vmax=1,edgecolors=None)
|
plt.scatter(x, y, c=alpha, marker='s', linewidths=0, cmap='Oranges', norm=pltcolors.Normalize(vmin=0, vmax=1),
|
||||||
|
vmin=0, vmax=1, edgecolors=None)
|
||||||
|
|
||||||
|
|
||||||
def plotComparedSeries(original, models, colors):
|
def plotComparedSeries(original, models, colors):
|
||||||
fig = plt.figure(figsize=[25,10])
|
fig = plt.figure(figsize=[15, 5])
|
||||||
ax = fig.add_subplot(111)
|
ax = fig.add_subplot(111)
|
||||||
|
|
||||||
mi = []
|
mi = []
|
||||||
@ -38,9 +42,16 @@ def plotComparedSeries(original,models, colors):
|
|||||||
ax.plot(original, color='black', label="Original")
|
ax.plot(original, color='black', label="Original")
|
||||||
count = 0
|
count = 0
|
||||||
for fts in models:
|
for fts in models:
|
||||||
|
if fts.hasPointForecasting:
|
||||||
forecasted = fts.forecast(original)
|
forecasted = fts.forecast(original)
|
||||||
|
mi.append(min(forecasted))
|
||||||
|
ma.append(max(forecasted))
|
||||||
|
for k in np.arange(0, fts.order):
|
||||||
|
forecasted.insert(0, None)
|
||||||
|
ax.plot(forecasted, color=colors[count], label=fts.shortname, ls="-")
|
||||||
|
|
||||||
if fts.isInterval:
|
if fts.hasIntervalForecasting:
|
||||||
|
forecasted = fts.forecastInterval(original)
|
||||||
lower = [kk[0] for kk in forecasted]
|
lower = [kk[0] for kk in forecasted]
|
||||||
upper = [kk[1] for kk in forecasted]
|
upper = [kk[1] for kk in forecasted]
|
||||||
mi.append(min(lower))
|
mi.append(min(lower))
|
||||||
@ -48,17 +59,11 @@ def plotComparedSeries(original,models, colors):
|
|||||||
for k in np.arange(0, fts.order):
|
for k in np.arange(0, fts.order):
|
||||||
lower.insert(0, None)
|
lower.insert(0, None)
|
||||||
upper.insert(0, None)
|
upper.insert(0, None)
|
||||||
ax.plot(lower,color=colors[count],label=fts.shortname)
|
ax.plot(lower, color=colors[count], label=fts.shortname,ls="--")
|
||||||
ax.plot(upper,color=colors[count])
|
ax.plot(upper, color=colors[count],ls="--")
|
||||||
|
|
||||||
else:
|
|
||||||
mi.append(min(forecasted))
|
|
||||||
ma.append(max(forecasted))
|
|
||||||
forecasted.insert(0,None)
|
|
||||||
ax.plot(forecasted,color=colors[count],label=fts.shortname)
|
|
||||||
|
|
||||||
handles0, labels0 = ax.get_legend_handles_labels()
|
handles0, labels0 = ax.get_legend_handles_labels()
|
||||||
ax.legend(handles0,labels0)
|
ax.legend(handles0, labels0, loc=2)
|
||||||
count = count + 1
|
count = count + 1
|
||||||
# ax.set_title(fts.name)
|
# ax.set_title(fts.name)
|
||||||
ax.set_ylim([min(mi), max(ma)])
|
ax.set_ylim([min(mi), max(ma)])
|
||||||
@ -76,7 +81,7 @@ def plotComparedIntervalsAhead(original,models, colors, distributions, time_from
|
|||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for fts in models:
|
for fts in models:
|
||||||
if fts.isDensity and distributions[count]:
|
if fts.hasDistributionForecasting and distributions[count]:
|
||||||
density = fts.forecastDistributionAhead(original[:time_from], time_to, 25)
|
density = fts.forecastDistributionAhead(original[:time_from], time_to, 25)
|
||||||
for k in density.index:
|
for k in density.index:
|
||||||
alpha = np.array([density[x][k] for x in density]) * 100
|
alpha = np.array([density[x][k] for x in density]) * 100
|
||||||
@ -85,7 +90,7 @@ def plotComparedIntervalsAhead(original,models, colors, distributions, time_from
|
|||||||
ax.scatter(x, y, c=alpha, marker='s', linewidths=0, cmap='Oranges',
|
ax.scatter(x, y, c=alpha, marker='s', linewidths=0, cmap='Oranges',
|
||||||
norm=pltcolors.Normalize(vmin=0, vmax=1), vmin=0, vmax=1, edgecolors=None)
|
norm=pltcolors.Normalize(vmin=0, vmax=1), vmin=0, vmax=1, edgecolors=None)
|
||||||
|
|
||||||
if fts.isInterval:
|
if fts.hasIntervalForecasting:
|
||||||
forecasts = fts.forecastAhead(original[:time_from], time_to)
|
forecasts = fts.forecastAhead(original[:time_from], time_to)
|
||||||
lower = [kk[0] for kk in forecasts]
|
lower = [kk[0] for kk in forecasts]
|
||||||
upper = [kk[1] for kk in forecasts]
|
upper = [kk[1] for kk in forecasts]
|
||||||
@ -130,6 +135,7 @@ def plotCompared(original,forecasts,labels,title):
|
|||||||
ax.set_xlim([0, len(original)])
|
ax.set_xlim([0, len(original)])
|
||||||
ax.set_ylim([min(original), max(original)])
|
ax.set_ylim([min(original), max(original)])
|
||||||
|
|
||||||
|
|
||||||
def SelecaoKFold_MenorRMSE(original, parameters, modelo):
|
def SelecaoKFold_MenorRMSE(original, parameters, modelo):
|
||||||
nfolds = 5
|
nfolds = 5
|
||||||
ret = []
|
ret = []
|
||||||
@ -250,6 +256,7 @@ def SelecaoKFold_MenorRMSE(original,parameters,modelo):
|
|||||||
ret.append(forecasted_best)
|
ret.append(forecasted_best)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def SelecaoSimples_MenorRMSE(original, parameters, modelo):
|
def SelecaoSimples_MenorRMSE(original, parameters, modelo):
|
||||||
ret = []
|
ret = []
|
||||||
errors = []
|
errors = []
|
||||||
@ -332,6 +339,7 @@ def SelecaoSimples_MenorRMSE(original,parameters,modelo):
|
|||||||
ret.append(forecastedd_best)
|
ret.append(forecastedd_best)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def compareModelsPlot(original, models_fo, models_ho):
|
def compareModelsPlot(original, models_fo, models_ho):
|
||||||
fig = plt.figure(figsize=[13, 6])
|
fig = plt.figure(figsize=[13, 6])
|
||||||
fig.suptitle("Comparação de modelos ")
|
fig.suptitle("Comparação de modelos ")
|
||||||
@ -346,6 +354,7 @@ def compareModelsPlot(original,models_fo,models_ho):
|
|||||||
handles0, labels0 = ax0.get_legend_handles_labels()
|
handles0, labels0 = ax0.get_legend_handles_labels()
|
||||||
ax0.legend(handles0, labels0)
|
ax0.legend(handles0, labels0)
|
||||||
|
|
||||||
|
|
||||||
def compareModelsTable(original, models_fo, models_ho):
|
def compareModelsTable(original, models_fo, models_ho):
|
||||||
fig = plt.figure(figsize=[12, 4])
|
fig = plt.figure(figsize=[12, 4])
|
||||||
fig.suptitle("Comparação de modelos ")
|
fig.suptitle("Comparação de modelos ")
|
||||||
@ -392,8 +401,10 @@ def compareModelsTable(original,models_fo,models_ho):
|
|||||||
|
|
||||||
return sup + header + body + "\\end{tabular}"
|
return sup + header + body + "\\end{tabular}"
|
||||||
|
|
||||||
|
|
||||||
from pyFTS import hwang
|
from pyFTS import hwang
|
||||||
|
|
||||||
|
|
||||||
def HOSelecaoSimples_MenorRMSE(original, parameters, orders):
|
def HOSelecaoSimples_MenorRMSE(original, parameters, orders):
|
||||||
ret = []
|
ret = []
|
||||||
errors = np.array([[0 for k in range(len(parameters))] for kk in range(len(orders))])
|
errors = np.array([[0 for k in range(len(parameters))] for kk in range(len(orders))])
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class FLR:
|
class FLR:
|
||||||
def __init__(self, LHS, RHS):
|
def __init__(self, LHS, RHS):
|
||||||
self.LHS = LHS
|
self.LHS = LHS
|
||||||
self.RHS = RHS
|
self.RHS = RHS
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.LHS) + " -> " + str(self.RHS)
|
return self.LHS.name + " -> " + self.RHS.name
|
||||||
|
|
||||||
|
|
||||||
def generateNonRecurrentFLRs(fuzzyData):
|
def generateNonRecurrentFLRs(fuzzyData):
|
||||||
flrs = {}
|
flrs = {}
|
||||||
|
@ -20,7 +20,7 @@ class FuzzySet:
|
|||||||
return self.mf(x, self.parameters)
|
return self.mf(x, self.parameters)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name + ": " + str(self.mf) + "(" + str(self.parameters) + ")"
|
return self.name + ": " + str(self.mf.__name__) + "(" + str(self.parameters) + ")"
|
||||||
|
|
||||||
|
|
||||||
def fuzzyInstance(inst, fuzzySets):
|
def fuzzyInstance(inst, fuzzySets):
|
||||||
|
23
fts.py
23
fts.py
@ -10,9 +10,11 @@ class FTS:
|
|||||||
self.shortname = name
|
self.shortname = name
|
||||||
self.name = name
|
self.name = name
|
||||||
self.detail = name
|
self.detail = name
|
||||||
self.isSeasonal = False
|
self.hasSeasonality = False
|
||||||
self.isInterval = False
|
self.hasPointForecasting = True
|
||||||
self.isDensity = False
|
self.hasIntervalForecasting = False
|
||||||
|
self.hasDistributionForecasting = False
|
||||||
|
self.dump = False
|
||||||
|
|
||||||
def fuzzy(self, data):
|
def fuzzy(self, data):
|
||||||
best = {"fuzzyset": "", "membership": 0.0}
|
best = {"fuzzyset": "", "membership": 0.0}
|
||||||
@ -28,6 +30,21 @@ class FTS:
|
|||||||
def forecast(self, data):
|
def forecast(self, data):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def forecastInterval(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def forecastDistribution(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def forecastAhead(self, data, steps):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def forecastAheadInterval(self, data, steps):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def forecastAheadDistribution(self, data, steps):
|
||||||
|
pass
|
||||||
|
|
||||||
def train(self, data, sets):
|
def train(self, data, sets):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
6
hofts.py
6
hofts.py
@ -1,6 +1,6 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pyFTS.common import FuzzySet,FLR
|
from pyFTS.common import FuzzySet,FLR
|
||||||
import fts
|
from pyFTS import fts
|
||||||
|
|
||||||
|
|
||||||
class HighOrderFLRG:
|
class HighOrderFLRG:
|
||||||
@ -18,7 +18,7 @@ class HighOrderFLRG:
|
|||||||
if len(self.strlhs) == 0:
|
if len(self.strlhs) == 0:
|
||||||
for c in self.LHS:
|
for c in self.LHS:
|
||||||
if len(self.strlhs) > 0:
|
if len(self.strlhs) > 0:
|
||||||
self.strlhs = self.strlhs + ", "
|
self.strlhs += ", "
|
||||||
self.strlhs = self.strlhs + c.name
|
self.strlhs = self.strlhs + c.name
|
||||||
return self.strlhs
|
return self.strlhs
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ class HighOrderFTS(fts.FTS):
|
|||||||
self.sets = sets
|
self.sets = sets
|
||||||
for s in self.sets: self.setsDict[s.name] = s
|
for s in self.sets: self.setsDict[s.name] = s
|
||||||
tmpdata = FuzzySet.fuzzySeries(data, sets)
|
tmpdata = FuzzySet.fuzzySeries(data, sets)
|
||||||
flrs = FuzzySet.generateRecurrentFLRs(tmpdata)
|
flrs = FLR.generateRecurrentFLRs(tmpdata)
|
||||||
self.flrgs = self.generateFLRG(flrs)
|
self.flrgs = self.generateFLRG(flrs)
|
||||||
|
|
||||||
def getMidpoints(self, flrg):
|
def getMidpoints(self, flrg):
|
||||||
|
2
hwang.py
2
hwang.py
@ -1,6 +1,6 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pyFTS.common import FuzzySet,FLR,Transformations
|
from pyFTS.common import FuzzySet,FLR,Transformations
|
||||||
import fts
|
from pyFTS import fts
|
||||||
|
|
||||||
|
|
||||||
class HighOrderFTS(fts.FTS):
|
class HighOrderFTS(fts.FTS):
|
||||||
|
5
ifts.py
5
ifts.py
@ -1,6 +1,6 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pyFTS.common import FuzzySet,FLR
|
from pyFTS.common import FuzzySet,FLR
|
||||||
import hofts, fts, tree
|
from pyFTS import hofts, fts, tree
|
||||||
|
|
||||||
|
|
||||||
class IntervalFTS(hofts.HighOrderFTS):
|
class IntervalFTS(hofts.HighOrderFTS):
|
||||||
@ -10,7 +10,8 @@ class IntervalFTS(hofts.HighOrderFTS):
|
|||||||
self.name = "Interval FTS"
|
self.name = "Interval FTS"
|
||||||
self.detail = "Silva, P.; Guimarães, F.; Sadaei, H. (2016)"
|
self.detail = "Silva, P.; Guimarães, F.; Sadaei, H. (2016)"
|
||||||
self.flrgs = {}
|
self.flrgs = {}
|
||||||
self.isInterval = True
|
self.hasPointForecasting = False
|
||||||
|
self.hasIntervalForecasting = True
|
||||||
|
|
||||||
def getUpper(self, flrg):
|
def getUpper(self, flrg):
|
||||||
if flrg.strLHS() in self.flrgs:
|
if flrg.strLHS() in self.flrgs:
|
||||||
|
@ -10,17 +10,21 @@ from pyFTS.common import FuzzySet, Membership
|
|||||||
def GridPartitionerTrimf(data, npart, names=None, prefix="A"):
|
def GridPartitionerTrimf(data, npart, names=None, prefix="A"):
|
||||||
sets = []
|
sets = []
|
||||||
dmax = max(data)
|
dmax = max(data)
|
||||||
dmax += dmax * 0.10
|
dmax += dmax * 0.1
|
||||||
|
print(dmax)
|
||||||
dmin = min(data)
|
dmin = min(data)
|
||||||
dmin -= dmin * 0.10
|
dmin -= dmin * 0.1
|
||||||
|
print(dmin)
|
||||||
dlen = dmax - dmin
|
dlen = dmax - dmin
|
||||||
partlen = math.ceil(dlen / npart)
|
partlen = math.ceil(dlen / npart)
|
||||||
partition = math.ceil(dmin)
|
#p2 = partlen / 2
|
||||||
for c in range(npart):
|
#partition = dmin #+ partlen
|
||||||
|
count = 0
|
||||||
|
for c in np.arange(dmin, dmax, partlen):
|
||||||
sets.append(
|
sets.append(
|
||||||
FuzzySet.FuzzySet(prefix + str(c), Membership.trimf, [round(partition - partlen, 3), partition, partition + partlen],
|
FuzzySet.FuzzySet(prefix + str(count), Membership.trimf, [c - partlen, c, c + partlen],c))
|
||||||
partition))
|
count += 1
|
||||||
partition += partlen
|
#partition += partlen
|
||||||
|
|
||||||
return sets
|
return sets
|
||||||
|
|
||||||
|
28
pfts.py
28
pfts.py
@ -2,21 +2,21 @@ import numpy as np
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import math
|
import math
|
||||||
from pyFTS.common import FuzzySet, FLR
|
from pyFTS.common import FuzzySet, FLR
|
||||||
import hofts, ifts, tree
|
from pyFTS import hofts, ifts, tree
|
||||||
|
|
||||||
|
|
||||||
class ProbabilisticFLRG(hofts.HighOrderFLRG):
|
class ProbabilisticFLRG(hofts.HighOrderFLRG):
|
||||||
def __init__(self, order):
|
def __init__(self, order):
|
||||||
super(ProbabilisticFLRG, self).__init__(order)
|
super(ProbabilisticFLRG, self).__init__(order)
|
||||||
self.RHS = {}
|
self.RHS = {}
|
||||||
self.frequencyCount = 0
|
self.frequencyCount = 0.0
|
||||||
|
|
||||||
def appendRHS(self, c):
|
def appendRHS(self, c):
|
||||||
self.frequencyCount = self.frequencyCount + 1
|
self.frequencyCount += 1
|
||||||
if c.name in self.RHS:
|
if c.name in self.RHS:
|
||||||
self.RHS[c.name] = self.RHS[c.name] + 1
|
self.RHS[c.name] += 1
|
||||||
else:
|
else:
|
||||||
self.RHS[c.name] = 1
|
self.RHS[c.name] = 1.0
|
||||||
|
|
||||||
def getProbability(self, c):
|
def getProbability(self, c):
|
||||||
return self.RHS[c] / self.frequencyCount
|
return self.RHS[c] / self.frequencyCount
|
||||||
@ -38,23 +38,27 @@ class ProbabilisticFTS(ifts.IntervalFTS):
|
|||||||
self.detail = "Silva, P.; Guimarães, F.; Sadaei, H."
|
self.detail = "Silva, P.; Guimarães, F.; Sadaei, H."
|
||||||
self.flrgs = {}
|
self.flrgs = {}
|
||||||
self.globalFrequency = 0
|
self.globalFrequency = 0
|
||||||
self.isInterval = True
|
self.hasPointForecasting = True
|
||||||
self.isDensity = True
|
self.hasIntervalForecasting = True
|
||||||
|
self.hasDistributionForecasting = True
|
||||||
|
|
||||||
def generateFLRG(self, flrs):
|
def generateFLRG(self, flrs):
|
||||||
flrgs = {}
|
flrgs = {}
|
||||||
l = len(flrs)
|
l = len(flrs)
|
||||||
for k in np.arange(self.order + 1, l):
|
for k in np.arange(self.order, l+1):
|
||||||
|
if self.dump: print("FLR: " + str(k))
|
||||||
flrg = ProbabilisticFLRG(self.order)
|
flrg = ProbabilisticFLRG(self.order)
|
||||||
|
|
||||||
for kk in np.arange(k - self.order, k):
|
for kk in np.arange(k - self.order, k):
|
||||||
flrg.appendLHS(flrs[kk].LHS)
|
flrg.appendLHS(flrs[kk].LHS)
|
||||||
|
if self.dump: print("LHS: " + str(flrs[kk]))
|
||||||
|
|
||||||
if flrg.strLHS() in flrgs:
|
if flrg.strLHS() in flrgs:
|
||||||
flrgs[flrg.strLHS()].appendRHS(flrs[k].RHS)
|
flrgs[flrg.strLHS()].appendRHS(flrs[k-1].RHS)
|
||||||
else:
|
else:
|
||||||
flrgs[flrg.strLHS()] = flrg;
|
flrgs[flrg.strLHS()] = flrg;
|
||||||
flrgs[flrg.strLHS()].appendRHS(flrs[k].RHS)
|
flrgs[flrg.strLHS()].appendRHS(flrs[k-1].RHS)
|
||||||
|
if self.dump: print("RHS: " + str(flrs[k-1]))
|
||||||
|
|
||||||
self.globalFrequency = self.globalFrequency + 1
|
self.globalFrequency = self.globalFrequency + 1
|
||||||
return (flrgs)
|
return (flrgs)
|
||||||
@ -68,9 +72,9 @@ class ProbabilisticFTS(ifts.IntervalFTS):
|
|||||||
def getMidpoints(self, flrg):
|
def getMidpoints(self, flrg):
|
||||||
if flrg.strLHS() in self.flrgs:
|
if flrg.strLHS() in self.flrgs:
|
||||||
tmp = self.flrgs[flrg.strLHS()]
|
tmp = self.flrgs[flrg.strLHS()]
|
||||||
ret = sum(np.array([tmp.getProbability(s) * self.setsDict[s].midpoint for s in tmp.RHS]))
|
ret = sum(np.array([tmp.getProbability(s) * self.setsDict[s].centroid for s in tmp.RHS]))
|
||||||
else:
|
else:
|
||||||
ret = sum(np.array([0.33 * s.midpoint for s in flrg.LHS]))
|
ret = sum(np.array([0.33 * s.centroid for s in flrg.LHS]))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def getUpper(self, flrg):
|
def getUpper(self, flrg):
|
||||||
|
2
sfts.py
2
sfts.py
@ -27,7 +27,7 @@ class SeasonalFTS(fts.FTS):
|
|||||||
self.name = "Seasonal FTS"
|
self.name = "Seasonal FTS"
|
||||||
self.detail = "Chen"
|
self.detail = "Chen"
|
||||||
self.seasonality = 1
|
self.seasonality = 1
|
||||||
self.isSeasonal = True
|
self.hasSeasonality = True
|
||||||
|
|
||||||
def generateFLRG(self, flrs):
|
def generateFLRG(self, flrs):
|
||||||
flrgs = []
|
flrgs = []
|
||||||
|
Loading…
Reference in New Issue
Block a user