I have code that has a structure similar to the following, in pseudocode. In the pseudocode example and has been written class Uno
to def main()
give an idea of the general structure of the real code I use and to try to convey the idea that there will be assignment of values from inside to outside the class and vice versa. I have decided on this approach by trying to come up with a minimal example that is both minimally consistent for the purpose of the question.
from numpy import mean
x = []
nueva_x = [1,1]
class Uno:
mi_lista=[[1,1],[1,1],[0,1],[0,1],[0,0],[1,0]]
for i in mi_lista:
x.append(i)
print(nueva_x)
def main():
variable_1 = [1,2,3]
That is, I initially declare a variable x
that is an empty list and a variable nueva_x
, which is a list that initially contains an array of values. The list x
successively encloses the lists inside mi_lista
.
What I'm looking for is:
that the list
x
goes attaching the listsmi_lista
for the same number of iterations that elements havevariable_1
(in example 3).once they have been attached to
x
a number of lists equal tolen(variable_1)
, and only then, compute the mean of the values of the lists attached tox
and assign it tonueva_x
. In the example, once youx
have attached 3 listsx=[[1,1],[1,1],[0,1]]
, we calculate the mean like thismedia_x=mean(x, 0)
.
The goal is that when you run the actual code, the nueva_x
inside of the class takes the initial value until the first "n" iterations are done (in example 3). From then on nueva_x
it will take its new value, and so on until the criteria of step 1 is met again. And so on.
My attempts so far have gone through cumbersome functions. But I'm sure there has to be an easy way to limit the number of lists that are attached with some function like append
. Once this is done, I would like to know how to proceed with the substitution and assignment of the new value to new_x
, as well as how to format the list x
.
Thanks for the comments in advance.
Update with a real code (6/11/2019)
Next we see an example with a code more approximate to the real code. This is a minimal example of the actual code (I share the code below).
Context:
As can be seen when running the simulator, the game ( juego.jugar
) iterates through ronda
in the emparejamientos
. In this way, when the execution reaches the key point , the one corresponding to the one (taken from ) that is being simulated at that moment def choose
is printed . As can be seen , it is a list with values from 0 to 1.self.sigma
jugador
self.nombre
self.sigma
Goal
We want to create a variable i
that attaches the self.sigma lists of each "player" for the same number of iterations that jugadores
we have. In the present case, 4.
Once i
a number of lists equal to has been appended to len(jugadores)
, and only then, compute the mean of the values of the lists appended to i and assign it to nueva_i
. In the example, once you i
have attached 4 lists i=[[1,1,0,0],[1,1,0,0],[0,0,1,1],[0,0,1,1]]
, we calculate the mean like this media_i=mean(i, 0)
.
Since the mean of i
in each round, that is , is nueva_i
intended to be used in the future to make a series of calculations throughout the simulation within def choose
, this list nueva_i
must have a value assigned prior to each round (its initial value will be the of the means of the initial sigmas assigned in main()
). In turn, in a future version of the code, within def_choose
, this nueva_i
will interfere in each round with that self.sigma
of each player, modifying it. In short, you need to nueva_i
collect the average of self.sigmas
the previous round. Each player will have their self.sigma
own, but nueva_i
it will be common to all players in each round. All of these concatenated things explain my initial attempt at working with a global variablei
to pass to nueva_i
the average of those self.sigma
of the players and then formatted from round to round.
from __future__ import division
from random import random
from bisect import bisect
from collections import deque, Counter
i=[]
nueva_i=[]
def choice(opciones, probs):
probAcumuladas = list()
aux = 0
for p in probs:
aux += p
probAcumuladas.append(aux)
r = random() * probAcumuladas[-1]
op = bisect(probAcumuladas, r)
return opciones[op]
class Jugador:
def __init__(self, nombre, senales, sigma, b, x, m, menLen):
self.nombre = nombre
self.senales = senales
self.mem_mostradas = {senal: 0 for senal in senales}
self.men_observadas = {senal: 0 for senal in senales}
self.__mem_mostradas = deque(maxlen=menLen)
self.__men_observadas = deque(maxlen=menLen)
self.sigma = sigma[:]
self.b = b
self.x = x
self.m = m
def memoriza(self, mostrada, observada):
self.__mem_mostradas.append(mostrada)
self.__men_observadas.append(observada)
mostradas = Counter(self.__mem_mostradas)
observadas = Counter(self.__men_observadas)
self.mem_mostradas = { signal: mostradas.get(signal, 0) for signal in self.senales }
self.mem_observadas = { signal: observadas.get(signal, 0) for signal in self.senales }
def __str__(self):
return "Jugador_{}".format(self.nombre)
def with_b(self, muestra, observa, r, idx):
if not (muestra == observa == 0):
result = (
((0.98) * (1.0 - self.b) * (1.0 - self.x) * muestra / r)
+ ((0.98) * (1.0 - self.b) * (self.x) * observa / r)
+ ((0.98) * self.b * self.sigma[idx])
+ ((self.m / 8))
)
else:
result = (
((0.98) * (1.0 - 0) * (1.0 - self.x) * muestra / r)
+ ((0.98) * (1.0 - 0) * (self.x) * observa / r)
+ ((0.98) * 0 * self.sigma[idx])
+ ((self.m / 8))
)
return result
def choose(self, r):
probs = [
self.with_b(
self.mem_mostradas[op], self.men_observadas[op], r, indx
)
for indx, op in enumerate(self.senales)
]
elecc = choice(self.senales, probs)
#Aquí es donde se necesitará llamar a self.sigma y nueva_i
print(self.sigma)
print(nueva_i)
return elecc
class Partida:
def __init__(self, jugadores, emparejamientos, senales, sigmas, b, x, m, menLen):
self.emparejamientos = emparejamientos
self.senales = senales
self.jugadores = {
nombre: Jugador(nombre, senales, sigmas[nombre], b, x, m, menLen)
for nombre in jugadores
}
self.memoria = list()
self.entropy = float()
def generar_senales(self):
yield dict(zip(self.jugadores, self.senales))
r = 1
while True:
eleccs = {}
for jugador in self.jugadores.values():
eleccs[jugador.nombre] = jugador.choose(r)
r += 1
yield eleccs
def jugar(self):
gen_sens = self.generar_senales()
for ronda in self.emparejamientos:
senales = next(gen_sens)
self.memoria.append(senales)
for jugador1, jugador2 in ronda:
self.jugadores[jugador1].memoriza(observada=senales[jugador2], mostrada=senales[jugador1])
self.jugadores[jugador2].memoriza(observada=senales[jugador1], mostrada=senales[jugador2])
def main():
jugadores = [1, 2, 3, 4]
senales = ["S1", "S2", "S3", "S4"]
emparejamientos = [[(1, 2), (3, 4)], [(1, 3), (2, 4)], [(1, 4), (2, 3)]]
patron = 1
menLen=3
s1 = [1, 0, 0, 0]
s2 = [0, 0, 0, 1]
sigmas = {1: s1, 2: s1, 3: s2, 4: s2}
muestras = [{"b": 0.0, "x": 0.5, "m": 0.02}]
muestras = [d for d in muestras for _ in range(1)]
simulaciones = 1
for sim in range(simulaciones):
for mu in range(len(muestras)):
juego = Partida(
jugadores,
emparejamientos,
senales,
sigmas,
muestras[mu]["b"],
muestras[mu]["x"],
muestras[mu]["m"],
menLen
)
juego.jugar()
if __name__ == "__main__":
main()
The problem
I think that basically it is an XY problem, that is, the user has a need X, but asks for something else Y (because when trying to solve X, problem Y appeared). These types of questions usually take a long time, and a lot of messages are sent to finally clarify which was X and which was Y. After several comments, reissues of the question, etc. I have come to the following conclusion
The problem X that the user originally had would be:
But when going to implement this idea, the user ran into variable visibility issues. The list of players is in one place. The information of the "sigma" (which are the values whose average you want to calculate) are in another place, inside each player. And the function in which he intended to implement the averaging would be somewhere else (a global function) so he didn't know how to access the player data etc. Also, since the players then needed to access the result of that average, he decided to make that average a global variable.
Faced with this scenario, he did not know how to proceed, and raised the question And what would it be:
This problem Y is much more difficult to understand and solve, and that is why the question has been neglected for some time.
But now that problem X is clear, let's move on to its solution.
Solution
The use of global variables is discouraged. Any information that a function needs must be passed to it as a parameter. If we are using object-oriented programming, we can simplify a bit since in that case the functions are object methods and can access (via
self
) object attributes that would be like global variables, but specific to that object.Using this idea I propose:
nuevas_i
, the list with the averages, be done from a method of the classPartida
, since this class has in one of its attributes the list of players, which will allow it to iterate through that list and access the sigma of each player , without major problem.nuevas_i
resultant be saved as an attribute of the classPartida
, since this list is useful to all players (somehow they will all share it).choose
that needs that list, receive it as a parameter. In this case you cannot take it from the objectPartida
, because the functionchoose
belongs to each player.With these ideas the modifications to the code would be:
i
andnueva_i
(and put aimport numpy as np
for the later calculation of the average)choose()
so that it receives an additional parameter:nueva_i
Modify the constructor
Partida.__init__()
so that it initializesself.nueva_i
with the average of the initial sigmas:Modify the call to
choose()
to pass to itself.nueva_i
, in thePartida.generar_senales()
. The lines in question would look like this:Modify the method
Partida.jugar()
so that it is at the end of each round when it is updatedself.nueva_i
with the average in question. The new version of this method looks like this:And I think this is it! I am waiting for additional comments to see if I had understood problem X correctly (problem Y I almost gave up :-))