1
\$\begingroup\$

I am building a 2d dictionary with an altered list at the end Afterwords I want to find the min of each list. Is there a better way to do it? Better being faster or at least less of an eyesore.

#dummy function, will be replaced later so don't worry about it.
def alter(x, y, z,):
    a = list(x)
    for i, val in enumerate(x):
         a[i] = val + y + z
    return a

masterStats = {}
askStats = [0, 1, 2]
for numberServed in range(3):
    masterStats[numberServed] = {}
    for timeToServe in range(3):
        masterStats[numberServed][timeToServe] = alter(askStats, numberServed, timeToServe)

for numberServed in masterStats.keys():
    for timeToServe in masterStats[numberServed].keys():
        print(min(masterStats[numberServed][timeToServe]))


for numberServed in masterStats.keys():
    print(numberServed)
    for timeToServe in masterStats[numberServed].keys():
        print("    " + str(timeToServe))
        print("        " + str(masterStats[numberServed][timeToServe]))
\$\endgroup\$
1
\$\begingroup\$

Python's variable names are conventionally named lowercase_width_underscores.

Loop using .items() not .keys() if you will need the values, so your final loop can be:

for numberServed, numberServedStats in masterStats.items():
    print(numberServed)
    for timeToServe, timeToServeStats in numberServedStats,items():
        print("    " + str(timeToServe))
        print("        " + str(timeToServeStats))

The whole thing can be made more efficient by using numpy:

askStats = numpy.array([0, 1, 2])

x = askStats[None,None,:] # put askStats in the third dimension
y = numpy.arange(3)[:, None, None] # put number served in the first dimension
z = numpy.arange(3)[None, :, None] # put time to serve in the second dimension

stats = x + y + z # same as in alter

We create a new array by adding together three existing arrays. The [:,None, None] parts control how they are added. Essentially, the colon indicates the column the data will be spread across. The None indicates where the data will be duplicated. So numpy.arange(3)[:,None] gets treated like

[ 
    [0, 0, 0],
    [1, 1, 1],
    [2, 2, 2]
]

Whereas numpy.arange(3)[None, :] gets treated like

[
    [0, 1, 2],
    [0, 1, 2],
    [0, 1, 2]
]

This lets us do the complete loop and adjust function in just that one expression.

for line in stats.min(axis=2).flatten():
    print(line)

The min method reduces the 3 dimensional array to 2d array by minimizing along dimension 2. Flatten() converts that 2d array into one long one dimensional array.

for row_index, row in enumerate(stats):
    print(row_index)
    for col_index, col in enumerate(row):
        print("    ", col_index)
        print("        ", col)

It probably won't help much here, but for bigger cases using numpy will be more efficient.

\$\endgroup\$
  • \$\begingroup\$ You lost me on that second part, can you explain a bit of what you're doing with numpy and flatten? \$\endgroup\$ – EasilyBaffled Jun 7 '13 at 0:09
  • \$\begingroup\$ @EasilyBaffled, I've added some explanation. \$\endgroup\$ – Winston Ewert Jun 7 '13 at 0:24
  • \$\begingroup\$ So to alter it to accept larger data sets, I replace 3 with x in numpy.arange(x) but how do I alter the [None,:,None] to take any amount? \$\endgroup\$ – EasilyBaffled Jun 7 '13 at 1:12
  • \$\begingroup\$ And how is the alter function run in it? \$\endgroup\$ – EasilyBaffled Jun 7 '13 at 1:20
  • \$\begingroup\$ @EasilyBaffled, edited to hopefully clarify things a bit. \$\endgroup\$ – Winston Ewert Jun 7 '13 at 3:43
0
\$\begingroup\$

alter is equivalent to a list comprehension:

def alter(x, y, z):
    return [val + y + z for val in x]

collections.defaultdict(dict) is a convenient 2-level dictionary. You could also make use of itertools.product for 2D looping:

masterStats = collections.defaultdict(dict)
askStats = [0, 1, 2]
for numberServed, timeToServe in itertools.product(range(3), range(3)):
    masterStats[numberServed][timeToServe] = alter(askStats, numberServed, timeToServe)

Iterate over values():

for subdict in masterStats.values():
    for lst in subdict.values():
        print(min(lst))

Iterate over items(), like Winston already mentioned:

for numberServed, subdict in masterStats.items():
    print(numberServed)
    for timeToServe, lst in subdict.items():
        print("    " + str(timeToServe))
        print("        " + str(lst))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.