Kurt Peek
Kurt Peek

Reputation: 57451

In Python, assign entries of dictionaries dynamically

In the code below, I have a dictionary "John" which I assign a value to with a certain key ("phone number"):

John={}
statement='John'+"['phone number']=123456"
exec statement

The reason I did it with "exec" like above is because the actual dictionary "John" and "phone number" are supposed to vary within a for loop. This works, but is not very elegant; I'd rather like to do something similar to

vars()['John'+'_phone_number']=123456

because in this case, you don't run the risk of the "exec" statement failing if the syntax is not right. (What I'd like to achieve is similar to Matlab's "assignin" function, except for dictionary entries).

Is there a way of defining entries of a dictionary without using "exec"?

Upvotes: 1

Views: 125

Answers (3)

Kurt Peek
Kurt Peek

Reputation: 57451

Although it seems that the majority feels the way to go is using nested dictionaries - and I'll accept this answer - in my own code I decided to stick with the not-so-elegant way using the "exec" statement. The code - which actually is not for users and phone numbers, but is to determine and store mathematical attributes of certain curves - is shown below:

import numpy as np
import matplotlib.pyplot as plt

# CSV file generated from nDI by Data Export -> ASCII with "Export as CSV" ticked on
filename="lnb.1t_LNB-1_wedge_amplitude_extractions.csv"

# Read column headers (to be variable names)
with open(filename) as f:
    firstline = f.readline()                    # Read first line of csv
    firstline = firstline.replace("\n","")      # Remove new line characters
    firstline = firstline.replace(" ","")       # Remove spaces
    ColumnHeaders = firstline.split(",")        # Get array of column headers

# Read in the data (omitting the first row containing column headers)
data=np.loadtxt(filename,skiprows=1,delimiter=",")

N_leg=50        # Number of traces 'pure' gas leg and brine leg

# Assign the data to arrays, with names of the variables generated from column headers
Ind=0                                       # Initialize counter
for Var in ColumnHeaders:
    vars()[Var]=data[:,Ind]                 # Create an array for each column, named after the corresponding column header
    if Var.endswith("_evnt_amp"):           # For columns with headers containing the suffix '_evnt_amp' ('event amplitude'):
        Event=Var.strip("_evnt_amp")        # Define a string containing the event name
        vars()[Event]={}                    # Initialize an empty dictionary
        statement1=Event+"['gas leg amplitude']=np.mean(data[0:N_leg,Ind])"
        exec statement1
        statement2=Event+"['brine leg amplitude']=np.mean(data[-N_leg:-1,Ind])"
        exec statement2
        statement3=Event+"['gas to brine leg ratio']=np.mean(data[0:N_leg,Ind])/np.mean(data[-N_leg:-1,Ind])"
        exec statement3
##        vars()[Var+'_gas_leg']=data[0:N_leg,Ind]
##        vars()[Var+'_brine_leg']=data[-N_leg:-1,Ind]
##        vars()[Var.strip('_evnt_amp')+'_gas_leg_amplitude']=np.mean(data[0:N_leg,Ind])
##        vars()[Var.strip('_evnt_amp')+'_brine_leg_amplitude']=np.mean(data[-N_leg:-1,Ind])
    Ind=Ind+1                               # Increment counter

plt.close('all')

plt.figure(1)
plt.plot(Bin,ROSLU_T_evnt_amp,'c')
plt.plot(Bin,-DC_T_evnt_amp,'m')
plt.xlabel('Bin')
plt.ylabel('|Amplitude|')
plt.legend(['ROSLU_T','DC_T'])

plt.show()

The relevant part is the for loop with the "exec" statements. (I've commented out the 'obsolete' lines below them, which work by appending suffixes instead of creating a dictionary).

After running this code, I can for example at the command line type the name of an event, and read several of its attributes:

>>> ROSLU_T
{'gas to brine leg ratio': 2.1782322499999998, 'gas leg amplitude': 174.25857999999997, 'brine leg amplitude': 80.0}

For reference I've pasted a part of the input csv file below:

Track, Bin, ZEZ2A_T_evnt_amp,RO_T_evnt_amp,ROSLU_T_evnt_amp,DC_T_evnt_amp 1, 1, -1288.4139, 999.9983, 174.1523, -201.6915 1, 2, -1288.4139, 999.9983, 174.1523, -201.6915 1, 3, -1288.4139, 999.9983, 174.1523, -201.6915 1, 4, -1288.4139, 999.9983, 174.1523, -201.6915 1, 5, -1288.4139, 999.9983,

The plot generated by this code is shown below.

wedge model amplitude extractions

Upvotes: 0

Joran Beasley
Joran Beasley

Reputation: 113978

I think defaultdict is a bad idea here ... Im pretty sure all users should have the same keys (eg all users should be a phone number and email , or a user_id or the same keys for every user

users = {}
for name,phone,email in [("john","123","a@b"),("mary","432","b@b"),...]:
    users[name] = dict(email=email,phone=phone)

or if you are going to use default dict at least ensure that it has all the required fields of a user

def my_user_dict():
    return {"email":"","phone":""}

users = defaultdict(my_user_dict)

Upvotes: 1

g.d.d.c
g.d.d.c

Reputation: 47988

I think you should use a dictionary of dictionaries. You can even employ a defaultdict to avoid needing to precondition each entry for each individual user.

from collections import defaultdict

users = defaultdict(dict)
users['John']['phone number'] = 123456

Upvotes: 4

Related Questions