sc_odict
The “odict” class, combining features from an OrderedDict and a list/array.
Highlights
sc.odict(): flexible container representing the best-of-all-worlds across dicts, lists, and arraysobjdict: like an odict, but allows get/set via e.g.foo.barinstead offoo['bar']
Classes
| Name | Description |
|---|---|
| argparse | Ultra-simple argument parser |
| counter | Like collections.Counter, but with additional supported mathematical operations. |
| dictobj | Lightweight class to create an object that can also act like a dictionary. |
| objdict | An odict that acts like an object – allow keys to be set/retrieved by object |
| odict | Ordered dictionary with integer indexing |
argparse
sc_odict.argparse(parse=True, **kwargs)Ultra-simple argument parser
Accepts positional or keyword arguments, and converts them to the correct type. While Python’s built in argparse has more features (such as help for each argument), this allows single-line parsing of arguments.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| parse | bool | whether to parse the arguments immediately (default True) | True |
| **kwargs | dict | keyword arguments to add to the parser | {} |
Returns
| Name | Type | Description |
|---|---|---|
| args | objdict | a dictionary-like object with the arguments |
Examples:
# Option 1: Supply arguments directly
args = sc.argparse(iterations=10, output_file='results.csv')
# Option 2: Add arguments one by one
args = sc.argparse()
args.add(iterations=10)
args.add(output_file='results.csv')
args.parse()
# Command-line usage
python argparse_example.py 100 'data.csv'
python argparse_example.py 100 output_file='data.csv'
python argparse_example.py iterations=100 --output_file='data.csv'
# Result
args.iterations == 10
args.output_file == 'data.csv'New in version 3.2.6.
Methods
| Name | Description |
|---|---|
| add | Add an argument |
| parse | Parse the arguments into the dictionary |
add
sc_odict.argparse.add(**kwargs)Add an argument
parse
sc_odict.argparse.parse()Parse the arguments into the dictionary
counter
sc_odict.counter()Like collections.Counter, but with additional supported mathematical operations.
sc.counter has an “array” property, which converts the values to a NumPy array; methods not available to a Counter object are performed on the array instead.
Examples:
vals = [1,1,12,3,4,2,4,2,53,5,5,6,2,3,5]
counts = sc.counter(vals)
counts.max() # returns 3- New in version 3.2.3.
Attributes
| Name | Description |
|---|---|
| array | NumPy array of values |
dictobj
sc_odict.dictobj(*args, **kwargs)Lightweight class to create an object that can also act like a dictionary.
Example:
obj = sc.dictobj()
obj.a = 5
obj['b'] = 10
print(obj.items())For a more powerful alternative, see sc.objdict().
Note: because dictobj is halfway between a dict and an object, it can’t be automatically converted to a JSON (but will fail silently). Use to_json() instead.
- New in version 1.3.0.
- New in version 1.3.1: inherit from dict
- New in version 2.0.0: allow positional arguments
- New in version 3.0.0: “fromkeys” now a class method;
to_json()method - New in version 3.1.6: “copy” returns another dictobj
Methods
| Name | Description |
|---|---|
| copy | Create a shallow copy |
| fromkeys | Create a new dictobj from keys |
| to_json | Export the dictobj to JSON (NB: regular json.dumps() does not work) |
copy
sc_odict.dictobj.copy()Create a shallow copy
fromkeys
sc_odict.dictobj.fromkeys(*args, **kwargs)Create a new dictobj from keys
to_json
sc_odict.dictobj.to_json()Export the dictobj to JSON (NB: regular json.dumps() does not work)
objdict
sc_odict.objdict(*args, **kwargs)An odict that acts like an object – allow keys to be set/retrieved by object notation.
In general, operations that would normally act on attributes (e.g. obj.x = 3) instead act on dict keys (e.g. obj['x'] = 3). If you want to actually get/set an attribute, use obj.getattribute()/obj.setattribute().
For a lighter-weight example (an object that acts like a dict), see sc.dictobj().
Examples:
import sciris as sc
obj = sc.objdict(foo=3, bar=2)
obj.foo + obj.bar # Gives 5
for key in obj.keys(): # It's still a dict
obj[key] = 10
od = sc.objdict({'height':1.65, 'mass':59})
od.bmi = od.mass/od.height**2
od['bmi'] = od['mass']/od['height']**2 # Vanilla syntax still works
od.keys = 3 # This raises an exception (you can't overwrite the keys() method)Nested logic based in part on addict: https://github.com/mewwts/addict
For a lighter-weight equivalent (based on dict instead of odict), see sc.dictobj().
Methods
| Name | Description |
|---|---|
| delattribute | Delete attribute if truly desired |
| getattribute | Get attribute if truly desired |
| setattribute | Set attribute if truly desired |
delattribute
sc_odict.objdict.delattribute(name)Delete attribute if truly desired
getattribute
sc_odict.objdict.getattribute(name)Get attribute if truly desired
setattribute
sc_odict.objdict.setattribute(name, value, force=False)Set attribute if truly desired
odict
sc_odict.odict(*args, defaultdict=None, **kwargs)Ordered dictionary with integer indexing
An ordered dictionary, like the OrderedDict class, but supports list methods like integer indexing, key slicing, and item inserting. It can also replicate defaultdict behavior via the defaultdict argument.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | dict | convert an existing dict, e.g. sc.odict({'a':1}) |
() |
| defaultdict | class | if provided, create as a defaultdict as well | None |
| kwargs | dict | additional keyword arguments, e.g. sc.odict(a=1) |
{} |
Examples:
# Simple example
mydict = sc.odict(foo=[1,2,3], bar=[4,5,6]) # Assignment is the same as ordinary dictionaries
mydict['foo'] == mydict[0] # Access by key or by index
mydict[:].sum() == 21 # Slices are returned as numpy arrays by default
for i,key,value in mydict.enumitems(): # Additional methods for iteration
print(f'Item {i} is named {key} and has value {value}')
# Detailed example
foo = sc.odict({'ant':3,'bear':4, 'clam':6, 'donkey': 8}) # Create odict
bar = foo.sorted() # Sort the dict
assert bar['bear'] == 4 # Show get item by value
assert bar[1] == 4 # Show get item by index
assert (bar[0:2] == [3,4]).all() # Show get item by slice
assert (bar['clam':'donkey'] == [6,8]).all() # Show alternate slice notation
assert (bar[np.array([2,1])] == [6,4]).all() # Show get item by list
assert (bar[:] == [3,4,6,8]).all() # Show slice with everything
assert (bar[2:] == [6,8]).all() # Show slice without end
bar[3] = [3,4,5] # Show assignment by item
bar[0:2] = ['the', 'power'] # Show assignment by slice
bar[[0,3]] = ['hill', 'trip'] # Show assignment by list
bar.rename('clam','oyster') # Show rename
print(bar) # Print results
# Defaultdict examples
dd = sc.odict(a=[1,2,3], defaultdict=list)
dd['c'].append(4)
nested = sc.odict(a=0, defaultdict='nested') # Create a infinitely nested dictionary (NB: may behave strangely on IPython)
nested['b']['c']['d'] = 2Note: by default, integers are used as an alias to string keys, so cannot be used as keys directly. However, you can force regular-dict behavior using setitem(), and you can convert a dictionary with integer keys to an odict using sc.odict.makefrom(). If an odict has integer keys and the keys do not match the key positions, then the key itself will take precedence (e.g., od[3] is equivalent to dict(od)[3], not dict(od)[od.keys()[3]]). This usage is discouraged.
- New in version 1.1.0: “defaultdict” argument
- New in version 1.3.1: allow integer keys via
makefrom(); removedto_OD; performance improvements - New in version 2.0.1: allow deletion by index
- New in version 3.0.0: allow numeric indices; inherit from dict rather than OrderedDict
- New in version 3.1.1: allow steps in slices;
copy()now behaves as a standard dict
Methods
| Name | Description |
|---|---|
| append | Support an append method, like a list |
| copy | Make a (shallow) copy of the dict. |
| dcp | Shortcut to odict.copy(deep=True). |
| dict_items | Return an iterator (not a list) over items (as in Python 2). |
| dict_keys | Return an iterator (not a list) over keys (as in Python 2). |
| dict_values | Return an iterator (not a list) over values (as in Python 2). |
| disp | Print out flexible representation, short by default. |
| enumitems | Returns tuple of 3 things: index, key, value. |
| enumkeys | Shortcut for enumerate(odict.keys()). |
| enumvals | Shortcut for enumerate(odict.values()) |
| enumvalues | Alias for enumvals(). New in version 1.2.0. |
| export | Export the odict in a form that is valid Python code |
| filter | Find matching keys in the odict, and return a new odict |
| filtervals | Like filter, but filters by value rather than key |
| findbykey | Same as findkeys, but returns values instead |
| findbyval | Returns the key(s) that match a given value – reverse of findbykey, except here |
| findkeys | Find all keys that match a given pattern. By default uses regex, but other options |
| fromeach | Take a “slice” across all the keys of an odict, applying the same |
| getnested | Alias to sc.getnested(odict); see sc.makenested() for full documentation. New in version 1.2.0. |
| index | Return the index of a given key |
| insert | Function to do insert a key – note, computationally inefficient. |
| items | Return a list of items (as in Python 2). |
| iteritems | Alias to items() |
| iternested | Alias to sc.iternested(odict); see sc.makenested() for full documentation. New in version 1.2.0. |
| keys | Return a list of keys (as in Python 2), not a dict_keys object. |
| make | An alternate way of making or adding to an odict. |
| makefrom | Create an odict from entries in another dictionary. If keys is None, then |
| makenested | Alias to sc.makenested(odict); see sc.makenested() for full documentation. New in version 1.2.0. |
| map | Apply a function to each element of the odict, returning |
| pop | Allows pop to support strings, integers, slices, lists, or arrays |
| promote | Like promotetolist, but for odicts. |
| remove | Remove an item by key and do not return it |
| rename | Change a key name (note: not optimized for speed) |
| reverse | Reverse the order of an odict |
| reversed | Shortcut for making a copy of the sorted odict |
| setitem | Use regular dictionary setitem, rather than odict’s |
| setnested | Alias to sc.setnested(odict); see sc.makenested() for full documentation. New in version 1.2.0. |
| sort | Create a sorted version of the odict. |
| sorted | Shortcut for making a copy of the sorted odict – see sort() for options |
| toeach | The inverse of fromeach: partially reset elements within |
| update | Update dict contents; set _stale so _cached_keys is refreshed on next use |
| valind | Return the index of a given value |
| values | Return a list of values (as in Python 2). |
append
sc_odict.odict.append(key=None, value=None)Support an append method, like a list
copy
sc_odict.odict.copy(deep=False)Make a (shallow) copy of the dict.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| deep | bool | if True, do a deep rather than shallow copy | False |
Examples:
d1 = sc.odict(a=[1,2,3], b='foo')
d2 = d1.copy()
d3 = d1.copy(deep=True)
d1.pop('b') # affects d1 but not d2 or d3
d1[0].append(4) # affects d1 and d2 but not d3dcp
sc_odict.odict.dcp()Shortcut to odict.copy(deep=True).
- New in version 3.2.3.
dict_items
sc_odict.odict.dict_items()Return an iterator (not a list) over items (as in Python 2).
dict_keys
sc_odict.odict.dict_keys()Return an iterator (not a list) over keys (as in Python 2).
dict_values
sc_odict.odict.dict_values()Return an iterator (not a list) over values (as in Python 2).
disp
sc_odict.odict.disp(
maxlen=None,
showmultilines=True,
divider=False,
dividerthresh=10,
numindents=0,
sigfigs=5,
numformat=None,
maxitems=20,
**kwargs,
)Print out flexible representation, short by default.
Example:
z = sc.odict().make(keys=['a','b','c'], vals=[4.293487,3,6])
z.disp(sigfigs=3)
z.disp(numformat='%0.6f')enumitems
sc_odict.odict.enumitems(transpose=False)Returns tuple of 3 things: index, key, value.
If transpose=True, return a tuple of lists rather than a list of tuples.
enumkeys
sc_odict.odict.enumkeys(transpose=False)Shortcut for enumerate(odict.keys()).
If transpose=True, return a tuple of lists rather than a list of tuples.
enumvals
sc_odict.odict.enumvals(transpose=False)Shortcut for enumerate(odict.values())
If transpose=True, return a tuple of lists rather than a list of tuples.
enumvalues
sc_odict.odict.enumvalues(transpose=False)Alias for enumvals(). New in version 1.2.0.
export
sc_odict.odict.export(doprint=True, classname='odict')Export the odict in a form that is valid Python code
filter
sc_odict.odict.filter(keys=None, pattern=None, method=None, exclude=False)Find matching keys in the odict, and return a new odict
Filter the odict keys and return a new odict which is a subset. If keys is a list, then uses that for matching. If the first argument is a string, then treats as a pattern for matching using findkeys() <odict.findkeys.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| keys | list | the list of keys to keep (or exclude) | None |
| pattern | str | the pattern by which to match keys; see findkeys() <odict.findkeys for details |
None |
| method | str | the method by which to match keys; see findkeys() <odict.findkeys for details |
None |
| exclude | bool | if exclude=True, then exclude rather than include matches | False |
See also sort(), which includes filtering by position.
filtervals
sc_odict.odict.filtervals(value)Like filter, but filters by value rather than key
findbykey
sc_odict.odict.findbykey(pattern=None, method=None, first=True)Same as findkeys, but returns values instead
findbyval
sc_odict.odict.findbyval(value, first=True, strict=False)Returns the key(s) that match a given value – reverse of findbykey, except here uses exact matches to the value or values provided.
Example:
z = sc.odict({'dog':[2,3], 'cat':[4,6], 'mongoose':[4,6]})
z.findbyval([4,6]) # returns 'cat'
z.findbyval([4,6], first=False) # returns ['cat', 'mongoose']findkeys
sc_odict.odict.findkeys(pattern=None, method=None, first=None)Find all keys that match a given pattern. By default uses regex, but other options are ‘find’, ‘startswith’, ‘endswith’. Can also specify whether or not to only return the first result (default false). If the key is a tuple instead of a string, it will search each element of the tuple.
fromeach
sc_odict.odict.fromeach(ind=None, asdict=True)Take a “slice” across all the keys of an odict, applying the same operation to entry. The simplest usage is just to pick an index. However, you can also use it to apply a function to each key.
Example:
z = sc.odict({'a':array([1,2,3,4]), 'b':array([5,6,7,8])})
z.fromeach(2) # Returns array([3,7])
z.fromeach(ind=[1,3], asdict=True) # Returns odict({'a':array([2,4]), 'b':array([6,8])})getnested
sc_odict.odict.getnested(*args, **kwargs)Alias to sc.getnested(odict); see sc.makenested() for full documentation. New in version 1.2.0.
index
sc_odict.odict.index(value)Return the index of a given key
insert
sc_odict.odict.insert(pos=None, key=None, value=None)Function to do insert a key – note, computationally inefficient.
Example:
z = sc.odict()
z['foo'] = 1492
z.insert(1604)
z.insert(0, 'ganges', 1444)
z.insert(2, 'mekong', 1234)items
sc_odict.odict.items(transpose=False)Return a list of items (as in Python 2).
iteritems
sc_odict.odict.iteritems(transpose=False)Alias to items()
iternested
sc_odict.odict.iternested(*args, **kwargs)Alias to sc.iternested(odict); see sc.makenested() for full documentation. New in version 1.2.0.
keys
sc_odict.odict.keys()Return a list of keys (as in Python 2), not a dict_keys object.
make
sc_odict.odict.make(keys=None, vals=None, keys2=None, keys3=None, coerce='full')An alternate way of making or adding to an odict.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| keys | list / int | the list of keys to use | None |
| vals | list / arr |
the list of values to use | None |
| keys2 | list / int | for a second level of nesting | None |
| keys3 | list / int | for a third level of nesting | None |
| coerce | str | what types to coerce into being separate dict entries | 'full' |
Examples:
a = sc.odict().make(5) # Make an odict of length 5, populated with Nones and default key names
b = sc.odict().make('foo',34) # Make an odict with a single key 'foo' of value 34
c = sc.odict().make(['a','b']) # Make an odict with keys 'a' and 'b'
d = sc.odict().make(['a','b'], 0) # Make an odict with keys 'a' and 'b', initialized to 0
e = sc.odict().make(keys=['a','b'], vals=[1,2]) # Make an odict with 'a':1 and 'b':2
f = sc.odict().make(keys=['a','b'], vals=np.array([1,2])) # As above, since arrays are coerced into lists
g = sc.odict({'a':34, 'b':58}).make(['c','d'],[99,45]) # Add extra keys to an exising odict
h = sc.odict().make(keys=['a','b','c'], keys2=['A','B','C'], keys3=['x','y','z'], vals=0) # Make a triply nested odictNew in version 1.2.2: “coerce” argument
makefrom
sc_odict.odict.makefrom(
source=None,
include=None,
keynames=None,
force=True,
*args,
**kwargs,
)Create an odict from entries in another dictionary. If keys is None, then use all keys from the current dictionary.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| source | dict / list / etc |
the item(s) to convert to an odict | None |
| include | list | list of keys to include from the source dict in the odict (default: all) | None |
| keynames | list | names of keys if source is not a dict | None |
| force | bool | whether to force conversion to an odict even if e.g. the source has numeric keys | True |
Examples:
a = 'cat'
b = 'dog'
o = sc.odict.makefrom(source=locals(), include=['a','b']) # Make use of fact that variables are stored in a dictionary
d = {'a':'cat', 'b':'dog'}
o = sc.odict.makefrom(d) # Same as sc.odict(d)
l = ['cat', 'monkey', 'dog']
o = sc.odict.makefrom(source=l, include=[0,2], keynames=['a','b'])
d = {12:'monkeys', 3:'musketeers'}
o = sc.odict.makefrom(d)makenested
sc_odict.odict.makenested(*args, **kwargs)Alias to sc.makenested(odict); see sc.makenested() for full documentation. New in version 1.2.0.
map
sc_odict.odict.map(func=None)Apply a function to each element of the odict, returning a new odict with the same keys.
Example:
cat = sc.odict({'a':[1,2], 'b':[3,4]})
def myfunc(mylist): return [i**2 for i in mylist]
dog = cat.map(myfunc) # Returns odict({'a':[1,4], 'b':[9,16]})pop
sc_odict.odict.pop(key, *args, **kwargs)Allows pop to support strings, integers, slices, lists, or arrays
promote
sc_odict.odict.promote(obj=None)Like promotetolist, but for odicts.
Example:
od = sc.odict.promote(['There','are',4,'keys'])Note, in most cases sc.odict(obj) or sc.odict().make(obj) can be used instead.
remove
sc_odict.odict.remove(key, *args, **kwargs)Remove an item by key and do not return it
rename
sc_odict.odict.rename(oldkey, newkey)Change a key name (note: not optimized for speed)
reverse
sc_odict.odict.reverse(copy=False)Reverse the order of an odict
reversed
sc_odict.odict.reversed()Shortcut for making a copy of the sorted odict
setitem
sc_odict.odict.setitem(key, value)Use regular dictionary setitem, rather than odict’s
setnested
sc_odict.odict.setnested(*args, **kwargs)Alias to sc.setnested(odict); see sc.makenested() for full documentation. New in version 1.2.0.
sort
sc_odict.odict.sort(sortby=None, reverse=False, copy=False)Create a sorted version of the odict.
By default, this method sorts alphabetically by key, but many other options are possible:
- 'keys' sorts alphabetically by key
- 'values' sorts in ascending order by value
- if a list of keys is provided, sort by that order (any keys not provided will be omitted from the sorted dict!)
- if a list of numbers is provided, treat these as indices and sort by that order
- if a list of boolean values is provided, then omit False entries
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| sortby | str or list | what to sort by; see above for options | None |
| reverse | bool | whether to return results in reverse order | False |
| copy | bool | whether to return a copy (same as sorted()) |
False |
For filtering by string matching on keys, see filter().
- New in version 3.0.0: removed “verbose” argument
sorted
sc_odict.odict.sorted(sortby=None, reverse=False)Shortcut for making a copy of the sorted odict – see sort() for options
toeach
sc_odict.odict.toeach(ind=None, val=None)The inverse of fromeach: partially reset elements within each odict key.
Example:
z = sc.odict({'a':[1,2,3,4], 'b':[5,6,7,8]})
z.toeach(2, [10,20]) # z is now odict({'a':[1,2,10,4], 'b':[5,6,20,8]})
z.toeach(ind=3,val=666) # z is now odict({'a':[1,2,10,666], 'b':[5,6,20,666]})update
sc_odict.odict.update(*args, **kwargs)Update dict contents; set _stale so _cached_keys is refreshed on next use
valind
sc_odict.odict.valind(value)Return the index of a given value
values
sc_odict.odict.values()Return a list of values (as in Python 2).
Functions
| Name | Description |
|---|---|
| asobj | Convert any object for which you would normally do a['b'] to one where you |
asobj
sc_odict.asobj(obj, strict=True)Convert any object for which you would normally do a['b'] to one where you can do a.b.
Note: this may lead to unexpected behavior in some cases. Use at your own risk. At minimum, objects created using this function have an extremely odd type – namely sciris.sc_odict.asobj.<locals>.objobj.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| obj | anything |
the object you want to convert | required |
| strict | bool | whether to raise an exception if an attribute is being set (instead of a key) | True |
Example:
d = dict(foo=1, bar=2)
d_obj = sc.asobj(d)
d_obj.foo = 10New in version 1.0.0.