Regarding dictionary value manipulation in python - python

CONTEXT:
The code is to be used for representing graphs for use in implementations of graph search algorithms (like Breadth-First Search).
I want to store the graph in form of a dictionary, where keys represent the nodes and each key has three corresponding values. First is a set of nodes with which the "key" shares an edge. Second is a Boolean flag for showing visited/not visited. Third is distance of the "key" from starting node.
"""
The 'test.txt' file contains the following:
1 2 3 4 5
2 1 3 4 5
3 1 2 5
4 1 2
5 1 2 3
"""
import math as m
def readGraph(path):
a = {}
file = open(path)
data = file.readlines()
for line in data:
items = line.split()
items = [int(i) for i in items]
a[items[0]] = items[1:len(items) + 1], 0, m.inf
return a
if __name__ == '__main__':
G = readGraph('test.txt')
print(G)
The dictionary (stored in 'G') for the given file is:
G = {1: ([2, 3, 4, 5], 0, inf), 2: ([1, 3, 4, 5], 0, inf), 3: ([1, 2, 5], 0, inf), 4: ([1, 2], 0, inf), 5: ([1, 2, 3], 0, inf)}
DOUBT:
Suppose now I want to change the second value of key 1, from 0 to 1.
Typing G[1] = G[1][0], 1, G[1][2] does not seem efficient.
Is there a better approach?
UPDATE:
I tried saving the dictionary entries as lists, but that is undesirable as it would change the format of dictionary, which I want to implement.
The following is a solution, but still I want to use the dictionary in its default form, with the elements of each key stored as tuple.
if __name__ == '__main__':
G = readGraph('test.txt')
print(G)
G[1] = list(G[1])
G[1][1] = 1
print(G)

One way of doing this, you can use nested dictionary to store the node. Here is how graph G will look like.
G = {
1: {
'nodes': [2, 3, 4, 5],
'is_visited': 0,
'distance': 'inf'
},
2: {
'nodes': [1, 3, 4, 5],
'is_visited': 0,
'distance': 'inf'
}
}
and then you can get values by indexing.
G[1]['is_visited'] = 1

You can store the values for each node as a list instead of a tuple:
a[items[0]] = [items[1:len(items) + 1], 0, m.inf]
and then just update the value you want directly:
G[1][1] = 1
Another option (thanks to Bilal for his suggestion of this approach) is nested dictionaries:
a[items[0]] = {"edges": items[1:len(items) + 1], "is_visited": 0, "dist": m.inf}
Then you could access the individual elements as:
G[1]["is_visited"] = 1

Related

Sort the item from two lists in one list while keeping the sequence in the original lists

I have two unsorted lists as followed:
A = [1, 3, 1.75]
B = [0, 1.5, 2, 4]
I want to make a list that includes the numbers in A and B in a sorted manner (e.g. ascending). However, I want to keep the sequence from each list as well. The suitable output would look like something below:
AB = [0, 1, 1.5, 2, 3, 1.75, 4]
Do you have any ideas/hints on how to do this? The original problem includes 150 lists that need to be merged into one list like above. Thank you for your ideas beforehand!
This looks like a "merge" problem to me:
def merge(lists):
iters = [iter(s) for s in lists]
heads = [next(s) for s in iters]
res = []
inf = float('inf')
while True:
v, n = min((v, n) for n, v in enumerate(heads))
if v == inf:
return res
res.append(v)
try:
heads[n] = next(iters[n])
except StopIteration:
heads[n] = inf
lists = [
[1,2,3,8],
[1,7,4],
[6,9,1,2,3],
]
print(merge(lists))
## [1, 1, 2, 3, 6, 7, 4, 8, 9, 1, 2, 3]

Combining multiple for loops that iterate over matrix python

hello I want to combine multiple for loops into one loop to make my code less complex.
the loop iterates over the elements in a list of lists and add +1 for every correct value.
so let's say i have
ink = 0
mouse = 0
rat = 0
matrix = [
['', 'article1.txt', 'article2.txt', 'article3.txt'], ['ink', 2, 3, 0], ['mouse', 0, 2, 1], ['rat', 0, 0, 1]]
and
for line in matrix[1:2]:
for i in line[1:]:
if i > 0:
ink = ink + 1
for line in matrix[2:3]:
for i in line[1:]:
if i > 0:
mouse = mouse + 1
for line in matrix[3:4]:
for i in line[1:]:
if i > 0:
rat = rat + 1
I'd want this to become one loop or atleast some shorter code that automatically does this, even if there would be more rows and columns in the matrix.
So assuming a correct value is a value greater than 1, i did this: Also to have infinite values, i organized the results in a dictionary:
matrix = [
["", "article1.txt", "article2.txt", "article3.txt"],
["ink", 2, 3, 0],
["mouse", 0, 2, 1],
["rat", 0, 0, 1]]
results = {product[0]: [1 for a in product[1:] if a > 0].count(1) for product in matrix[1:]}
print(results)
I hope this is what you expected. Let me know if you need any adjustments or explanations. This would be the result for your example:
{'ink': 2, 'mouse': 2, 'rat': 1}
One way to improve your code, would be:
>>> d = {}
>>> for row in matrix[1:]:
d[row[0]] = len(list(filter(lambda x: x>0, row[1:])))
>>> d
{'ink': 2, 'mouse': 2, 'rat': 1}
This also could be compacted into one liner dictionary comprehension:
>>> d = {row[0]:len(list(filter(lambda x: x>0, row[1:]))) for row in matrix[1:]}

Transform a Pandas series to be monotonic

I'm looking for a way to remove the points that ruin the monotonicity of a series.
For example
s = pd.Series([0,1,2,3,10,4,5,6])
or
s = pd.Series([0,1,2,3,-1,4,5,6])
we would extract
s = pd.Series([0,1,2,3,4,5,6])
NB: we assume that the first element is always correct.
Monotonic could be both increasing or decreasing, the functions below will return exclude all values that brean monotonicity.
However, there seems to be a confusion in your question, given the series s = pd.Series([0,1,2,3,10,4,5,6]), 10 doesn't break monotonicity conditions, 4, 5, 6 do. So the correct answer there is 0, 1, 2, 3, 10
import pandas as pd
s = pd.Series([0,1,2,3,10,4,5,6])
def to_monotonic_inc(s):
return s[s >= s.cummax()]
def to_monotonic_dec(s):
return s[s <= s.cummin()]
print(to_monotonic_inc(s))
print(to_monotonic_dec(s))
Output is 0, 1, 2, 3, 10 for increasing and 0 for decreasing.
Perhaps you want to find the longest monotonic array? because that's a completely different search problem.
----- EDIT -----
Below is a simple way of finding the longest monotonic ascending array given your constraints using plain python:
def get_longeset_monotonic_asc(s):
enumerated = sorted([(v, i) for i, v in enumerate(s) if v >= s[0]])[1:]
output = [s[0]]
last_index = 0
for v, i in enumerated:
if i > last_index:
last_index = i
output.append(v)
return output
s1 = [0,1,2,3,10,4,5,6]
s2 = [0,1,2,3,-1,4,5,6]
print(get_longeset_monotonic_asc(s1))
print(get_longeset_monotonic_asc(s2))
'''
Output:
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
'''
Note that this solution involves sorting which is O(nlog(n)) + a second step which is O(n).
Here is a way to produce a monotonically increasing series:
import pandas as pd
# create data
s = pd.Series([1, 2, 3, 4, 5, 4, 3, 2, 3, 4, 5, 6, 7, 8])
# find max so far (i.e., running_max)
df = pd.concat([s.rename('orig'),
s.cummax().rename('running_max'),
], axis=1)
# are we at or above max so far?
df['keep?'] = (df['orig'] >= df['running_max'])
# filter out one or many points below max so far
df = df.loc[ df['keep?'], 'orig']
# verify that remaining points are monotonically increasing
assert pd.Index(df).is_monotonic_increasing
# print(df.drop_duplicates()) # eliminates ties
print(df) # keeps ties
0 1
1 2
2 3
3 4
4 5
10 5 # <-- same as previous value -- a tie
11 6
12 7
13 8
Name: orig, dtype: int64
You can see graphically with s.plot(); and df.plot();

How to Transfer a number in an array from one position to another

I would like to know how to transfer a number.
For example: [1,0,2,3,4]
Remove the one and transfer the one to two's position
Result: [0,0,1,3,4]
If your manipulations are purely index-based, you can do this:
lst = [1,0,2,3,4]
lst[2] = lst[0]
lst[0] = 0
# [0, 0, 1, 3, 4]
Alternatively, if you need to work out the index of 2:
lst[lst.index(2)] = lst[0]
lst[0] = 0
Since you have not described your question with clear instructions, There is case when there will be more than one 2 or 1 in vector then what you want to do ?
My solution is only for that condition when there is single 1 and 2 in vector because when you use .index method it always returns first value index no matter there are other values too.
Since in your dataset there is always 1 times 1 and 2 in all vector so here is the solution for that
data=[[1, 2, 3, 4, 0], [1, 3, 2, 4, 0], [2, 1, 3, 4, 0] ]
def replace_ (vector_ , replace_value, replace_with):
memory=vector_.index(replace_with)
vector_[vector_.index(replace_value)]=vector_[vector_.index(replace_with)]
vector_[memory]=0
return vector_
for i in data:
print(replace_(i,1,2))
If there are more than one 1 or 2 in vector like [1,0,1,1,2,2] then describe your logic and edit your question for that.

Dictionary works for len(string) multiple of 3. Function deletes remainders but now doesn't translate with dictionary. Python 2.7.1

I made a function with a dictionary. The purpose of the function is to separate the input string into sets of 3 . If the input string value is not a multiple of 3, I want to delete the remainder [1 or 2]
my function was working perfectly until I added the part for deleting the remainders
def func(fx):
d={'AAA':1,'BBB':2,'CCC':3}
length=len(fx)
if length % 3 == 0:
return fx
if length % 3 == 1:
return fx[:-1]
if length % 3 == 2:
return fx[:-2]
Fx=fx.upper()
Fx3=[Fx[i:i+3] for i in range(0,len(Fx),3)]
translate=[d[x] for x in Fx3]
return translate
x='aaabbbcc'
output = func(x)
print output
>>>
aaabbb
the function is recognizing that the input sequence is not a multiple of 3 so its deleting the 2 values which is what i want. However, its splitting the new string into 3 letter words to be translated with my dictionary anymore. If you delete the if statements, the function works but only for strings that are a multiple of 3.
What am I doing wrong ???
You are returning fx when you probably should be reassigning it
def func(fx):
d={'AAA':1,'BBB':2,'CCC':3}
length=len(fx)
if length % 3 == 0:
pass
elif length % 3 == 1:
fx = fx[:-1]
elif length % 3 == 2:
fx = fx[:-2]
Fx=fx.upper()
Fx3=[Fx[i:i+3] for i in range(0,len(Fx),3)]
translate=[d[x] for x in Fx3]
return translate
Here is an alternate function for you to figure out when you know some more Python
def func(fx):
d = {'AAA':1,'BBB':2,'CCC':3}
return [d["".join(x).upper()] for x in zip(*[iter(fx)]*3)]
Does this do what you want?
def func(fx):
d = {'AAA': 1, 'BBB': 2, 'CCC': 3}
fx = fx[:-(len(fx)%3)].upper()
groups = [fx[i:i+3] for i in range(0, len(fx), 3)]
translate = [d[group] for group in groups]
return translate
x='aaabbbcc'
print func(x)
When trimming the end of the string, you were returning the result when you wanted to just store it in a variable or assign it back to fx.
Rather than the if .. elifs you can just use the result of the length modulo 3 directly.
There is no need of a function, it can be done in a one liner less complex than the gnibbler's one.
Acom's solution is nearly mine.
d={'AAA':1,'BBB':2,'CCC':3}
for fx in ('bbbcccaaabbbcccbbbcccaaabbbcc',
'bbbcccaaabbbaaa','bbbcccaaabbbaa','bbbcccaaabbba',
'bbbcccaaabbb','bbbcccaaabb','bbbcccaaab',
'bbbcccaaa','bbbcccaa','bbbccca',
'bbbccc','bbbcc','bbbc',
'bbb','bb','b',''):
print fx
print tuple( d[fx[i:i+3].upper()] for i in xrange(0, len(fx)-len(fx)%3, 3) )
produces
bbbcccaaabbbcccbbbcccaaabbbcc
(2, 3, 1, 2, 3, 2, 3, 1, 2)
bbbcccaaabbbaaa
(2, 3, 1, 2, 1)
bbbcccaaabbbaa
(2, 3, 1, 2)
bbbcccaaabbba
(2, 3, 1, 2)
bbbcccaaabbb
(2, 3, 1, 2)
bbbcccaaabb
(2, 3, 1)
bbbcccaaab
(2, 3, 1)
bbbcccaaa
(2, 3, 1)
bbbcccaa
(2, 3)
bbbccca
(2, 3)
bbbccc
(2, 3)
bbbcc
(2,)
bbbc
(2,)
bbb
(2,)
bb
()
b
()
()
.
I think you have to treat strings that can contain only 3 characters strings 'aaa','bbb','ccc' at the positions 0,3,6,9,etc
Then the preceding programs won't crash if there's an heterogenous 3-characters string at one of these positions instead of one of these set 'aaa','bbb','ccc'
In this case, note that you could use the dictionary's method get that returns a default value when a pased argument isn't a key of the dictionary.
In the following code, I put the default returned value as 0:
d={'AAA':1,'BBB':2,'CCC':3}
for fx in ('bbbcccaaa###bbbccc"""bbbcc',
'bbb aaabbbaaa','bbbccc^^^bbbaa','bbbc;;;aabbba',
'bbbc^caaabbb',']]bccca..bb','bbb%%%aaab',
'bbbcccaaa','bbb!ccaa','b#bccca',
'bbbccc','bbbcc','bbbc',
'b&b','bb','b',''):
print fx
print [d.get(fx[i:i+3].upper(), 0) for i in xrange(0, len(fx)-len(fx)%3, 3)]
produces
bbbcccaaa###bbbccc"""bbbcc
[2, 3, 1, 0, 2, 3, 0, 2]
bbb aaabbbaaa
[2, 0, 1, 2, 1]
bbbccc^^^bbbaa
[2, 3, 0, 2]
bbbc;;;aabbba
[2, 0, 0, 2]
bbbc^caaabbb
[2, 0, 1, 2]
]]bccca..bb
[0, 3, 0]
bbb%%%aaab
[2, 0, 1]
bbbcccaaa
[2, 3, 1]
bbb!ccaa
[2, 0]
b#bccca
[0, 3]
bbbccc
[2, 3]
bbbcc
[2]
bbbc
[2]
b&b
[0]
bb
[]
b
[]
[]
By the way, I preferred to create a tuple instead of a list because for the kind of invariable objects that are in the result, I think it is better not to create a list

Categories