Turning a sublist of a list into a dictionary - python

So I was wondering whether it's possible to get a sublist of a list into a dictionary.
For example, a list contains:
cy = [["a", "b", "a"], ["a", "b", "c", "d", "a"], ["c", "b", "a", "c"], ["d, "b", "a", "d"]]
would be stored into a dictionary according to the first letter of the sublist
{a : ["a", "b", "a"], c : ["c", "b", "a", "c"], d: ["d, "b", "a", "d"] }
Notice that it only stores the first sublist of the key that starts with the "a" and not the next.
My code is as follows:
def syn(graph,start):
empty = []
cy = [["a", "b", "a"], ["a", "b", "c", "d", "a"], ["c", "b", "a", "c"], ["d, "b", "a", "d"]]
lei = dict()
for items in cy:
if items[0] in lei:
lei[items[0]] += items
else:
lei[items[0]] = items
return lei
But all I get is
{a : ["a", "b", "a"]}
Is there any way to fix this?

With simple dict comprehension:
cy = [["a", "b", "a"], ["a", "b", "c", "d", "a"], ["c", "b", "a", "c"], ["d", "b", "a", "d"]]
result = {l[0]: l[:] for l in cy[::-1]}
print(result)
The output:
{'a': ['a', 'b', 'a'], 'c': ['c', 'b', 'a', 'c'], 'd': ['d', 'b', 'a', 'd']}
cy[::-1] - processing the input list in reversed order to prevent overlapping by same next "keys"

While you can very cleverly iterate backwards, as #juanpa.arrivillaga's and #RomanPerekhrest solutions demonstrate, another approach is to simple iterate over the list and user the any function:
cy = [["a", "b", "a"], ["a", "b", "c", "d", "a"], ["c", "b", "a", "c"], ["d", "b", "a", "d"]]
new_list = []
for i in cy:
if not any(i[0] == b[0] for b in new_list):
new_list.append(i)
final_dict = {i[0]:i for i in new_list}
Output:
{'a': ['a', 'b', 'a'], 'c': ['c', 'b', 'a', 'c'], 'd': ['d', 'b', 'a', 'd']}

The most straightforward way is to iterate over your list backwards, adding the list to the dict based on the first element. Since you go backwards, this guarantees that the first sublist will be the list contained in your dict:
>>> result = {}
>>> for sub in reversed(cy):
... result[sub[0]] = sub
...
>>> result
{'d': ['d', 'b', 'a', 'd'], 'a': ['a', 'b', 'a'], 'c': ['c', 'b', 'a', 'c']}
>>>
So, you would keep the last encountered value, but the last value of list in reverse order is the first value in forward order!

Using pandas:
import pandas as pd
cy = [["a", "b", "a"], ["a", "b", "c", "d", "a"], ["c", "b", "a", "c"], ["d", "b", "a", "d"]]
# create a df
df = pd.DataFrame([[i] for i in cy],[i[0] for i in cy])
# export it in reverse order
df.iloc[::-1].T.to_dict(orient='rows')[0]
Returns:
{'a': ['a', 'b', 'a'], 'c': ['c', 'b', 'a', 'c'], 'd': ['d', 'b', 'a', 'd']}

Related

Generating distinct partly-unordered permutations in Python

Given a list of length 2n, say ["a", "b", "c", "d", "e", "f"] or ["a", "a", "b", "b", "c", "d"] (elements in the list don't necessarily have to be unique), I'd like to generate all the possible distinct permutations of that list while taking into account that the order in which the element 2k and the element 2k+1 appear doesn't matter. That means that
["a", "b", "c", "d", "e", "f"] and ["b", "a", "c", "d", "e", "f"] are the same permutation, but
["a", "b", "c", "d", "e", "f"] and ["a", "c", "b", "d", "e", "f"] are not.
For example, from the list ["a", "b", "c", "d"], the code I need would generate this sequence:
["a", "b", "c", "d"], ["a", "c", "b", "d"], ["a", "d", "b", "c"], ["b", "c", "a", "d"], ["b", "d", "a", "c"], ["c", "d", "a", "b"]
I know it's possible to do that by generating all the permutations and keeping one of each of those that are equivalent to each other, but that's not a very efficient way to proceed, especially with larger sets. Is there a more efficient way to do that?
I wrote this code, but it's highly inefficient (keep in mind that I need to use lists with a length of up to 14):
from itertools import permutations
list_letters = ["a", "b", "c", "d"]
n = int(len(list_letters) / 2)
set_distinct_perm = set()
for perm in permutations(list_letters):
perm = list(perm)
for i in range(n):
perm[2*i:2*i + 2] = sorted(perm[2*i:2*i+2])
perm = tuple(perm)
set_distinct_perm.add(perm)
print(set_distinct_perm)
How about the following, where we take permutations of each [2k, 2k+1] (inclusive) subsequence and then take the product of those permutations:
from itertools import product, permutations
def equivalents(lst):
perms = product(*({*permutations(lst[i:i+2])} for i in range(0, len(lst), 2)))
return [[x for tupl in perm for x in tupl] for perm in perms] # flattening inner part
print(*equivalents('abcdef'))
# ['a', 'b', 'c', 'd', 'f', 'e'] ['b', 'a', 'c', 'd', 'f', 'e']
# ['a', 'b', 'c', 'd', 'e', 'f'] ['a', 'b', 'd', 'c', 'f', 'e']
# ['a', 'b', 'd', 'c', 'e', 'f'] ['b', 'a', 'd', 'c', 'e', 'f']
# ['b', 'a', 'd', 'c', 'f', 'e'] ['b', 'a', 'c', 'd', 'e', 'f']
print(*equivalents('aabbef'))
# ['a', 'a', 'b', 'b', 'f', 'e'] ['a', 'a', 'b', 'b', 'e', 'f']

Python: looping through a list at certain indices (containing certain string)

So I want to loop through a list starting at string "B" to the end or until a new "B" is reached and so on till the end of the list.
l=["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
print(index)
I want to loop through the list so I get these outputs but without hard coding the indices:
l[1:4]=['B', 'C', 'D']
l[4:8]=['B', 'M', 'C', 'T']
l[8:]=['B', 'g', 'do']
If just one index has that string then
l=["A", "B", "C", "D"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
print(index)
l[1:]=["B", "C", "D"]
Taking your code and using a for loop:
l = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
out_of_bounds_index = len(index)
result = []
for i in range(out_of_bounds_index):
if i+1 < out_of_bounds_index:
result.append(l[index[i]:index[i+1]])
else:
result.append(l[index[i]:])
print(result)
Or a list comprehension:
l = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
index = [idx for idx, s in enumerate(l) if 'B' in s]
out_of_bounds_index = len(index)
result = [l[index[i]:index[i+1]]
if i+1 < out_of_bounds_index
else l[index[i]:]
for i in range(out_of_bounds_index)]
print(result)
Output:
[['B', 'C', 'D'], ['B', 'M', 'C', 'T'], ['B', 'g', 'do']]
Try this:
def get_lists(l):
indices = [i for i, x in enumerate(l) if x == "B"]
if len(indices) == 0:
return []
elif len(indices) == 1:
return [[l[indices[0]]]]
else:
result = list()
for i in range(len(indices)):
if indices[i] == indices[-1]:
result.append(l[indices[i]:])
else:
result.append(l[indices[i]:indices[i+1]])
return result
k = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
print(get_lists(k))
Output:
[['B', 'C', 'D'], ['B', 'M', 'C', 'T'], ['B', 'g', 'do']]
l = ["A", "B", "C", "D", "B", "M", "C", "T", "B", "g", "do"]
#note that an extra 'B' is added to the list: l+['B']
indexOfB = [i for i, x in enumerate(l+['B']) if x == 'B']
Blists = [l[i[0]:i[1]] for i in zip(indexOfB, indexOfB[1:])]
[print(x) for x in Blists]

How to skip looping over list elements added to the list from inside a for-loop?

I am iterating over nested lists with a for-loop and would like to insert a new nested lists at the next index if a condition is met. During the next iterations these newly added lists should be skipped. Example:
input_lst = [["a", "b"], ["a", "d"], ["e", "f"]]
for sublst in input_lst:
if sublst[0] == "a":
new_sublst = []
input_lst.insert((input_lst.index(sublst) + 1), new_sublst)
new_sublst.insert(0, "a")
new_sublst.insert(1, "z")
print(input_lst)
Intended outcome:
[["a", "b"], ["a", "z"], ["a", "d"], ["a", "z"], ["e", "f"]]
Actual outcome - endless loop with:
[["a", "b"], ["a", "z"], ["a", "z"], ["a", "z"], ["a", "z"] ...
Try this :
input_lst = [["a", "b"], ["a", "d"], ["e", "f"]]
result = []
for sublst in input_lst:
result.append(sublst)
if sublst[0] == "a":
result.append(["a", "z"])
print(result)
Alternatively:
input_lst = [["a", "b"], ["a", "d"], ["e", "f"]]
for sublst in input_lst:
if sublst == ["a","z"]:
continue
if sublst[0] == "a":
new_sublst = []
input_lst.insert((input_lst.index(sublst) + 1), new_sublst)
new_sublst.insert(0, "a")
new_sublst.insert(1, "z")
print(input_lst)
Result:
[['a', 'b'], ['a', 'z'], ['a', 'd'], ['a', 'z'], ['e', 'f']]
Point to note:
If you add ["a","z"] as the next element in the list when current sublst start with "a", you created a endless-loop because next iteration is definitely starting with an "a" as in ["a", "z"].
Using a new list to store result of each iteration can solve the problem. Or you can skip adding another ["a","z"] when the current sublst is ["a","z"].
You could use a second list:
input_lst = [["a", "b"], ["a", "d"], ["e", "f"]]
new_sublst = ["a", "z"]
output_lst = []
for sublst in input_lst:
output_lst.append(sublst)
if sublst[0] == "a":
output_lst.append(new_sublst)
Output:
[['a', 'b'], ['a', 'z'], ['a', 'd'], ['a', 'z'], ['e', 'f']]
Adding to the answer of #ytung-dev, which tests against a specific condition, I came up with a solution that uses a second list as a negative to compare against. Maybe someone else will find this useful:
input_lst = [["a", "b"], ["a", "d"], ["e", "f"]]
input_lst_negative = []
for sublst in input_lst:
if (sublst[0] == "a") and (sublst not in input_lst_negative):
new_sublst = []
input_lst.insert((input_lst.index(sublst) + 1), new_sublst)
new_sublst.insert(0, "a")
new_sublst.insert(1, "z")
input_lst_negative.append(new_sublst)
print(input_lst)
Output:
[['a', 'b'], ['a', 'z'], ['a', 'd'], ['a', 'z'], ['e', 'f']]

Setting the colour of Networkx nodes with Python dict

I want to set the colour for specific nodes in networkx lib with Python.
These are my nodes:
node_list = ["A", "B", "C", "D", "E", "F"]
And these are the colours that I want:
A->red
B->red
C->red
D->green
E->green
F->blue
I do not know how to do this, because sometimes i have more than 200 different nodes so i am looking for some solution that will work with dict:
{"red":[A,B,C], "green": [D,E], "blue": [F]}
This is my code:
import networkx as nx
analysis_graph = nx.Graph()
node_list = ["A", "B", "C", "D", "E", "F"]
analysis_graph.add_nodes_from(node_list)
#print(analysis_graph.nodes())
nx.draw(analysis_graph, with_labels = True)
relation_list = [['A', 'B'],
['A', 'C'],
['B', 'D'],
['C', 'E'],
['D', 'F'],
['E', 'D'],
['C', 'E'],
['B', 'D'],
['C', 'F'],
['A', 'E'],
['B', 'C'],
['B', 'F'],
['D', 'A']]
analysis_graph = nx.from_edgelist(relation_list)
print(nx.info(analysis_graph))
nx.draw(analysis_graph, with_labels = True)
Sure. According to the docs nx.draw supports a node_color argument. You'll just have to explicitly list the nodes in a proper order:
relation_list = [
["A", "B"],
["A", "C"],
["B", "D"],
["C", "E"],
["D", "F"],
["E", "D"],
["C", "E"],
["B", "D"],
["C", "F"],
["A", "E"],
["B", "C"],
["B", "F"],
["D", "A"],
]
colors = {"red": ["A", "B", "C"], "green": ["D", "E"], "blue": ["F"]}
analysis_graph = nx.from_edgelist(relation_list)
# Create a mapping of node -> color from `colors`:
node_color_map = {}
for color, nodes in colors.items():
node_color_map.update(dict.fromkeys(nodes, color))
# Create a list of all the nodes to draw
node_list = sorted(nx.nodes(analysis_graph))
# ... and a list of colors in the proper order.
node_color_list = [node_color_map.get(node) for node in node_list]
# Draw the graph with the correct colors.
nx.draw(
analysis_graph,
with_labels=True,
nodelist=node_list,
node_color=node_color_list,
)
The output is e.g.

how to combine or leave strings in the lists depending on the condition in python?

I have three lists:
li1 = ["a", "a", "a", "a", "b", "b", "a", "a", "b"]
li2 = ["a", "a", "a", "b", "a,", "b", "a", "a"]
li3 = ["b", "b", "a", "a", "b"]
I want to "slice and paste" elements by "b"
The result is supposed to look like this:
li1 = ["aaaa", "b", "b", "aa", "b"]
li2 = ["aaa", "b", "a", "b", "aa"]
li3 = ["b", "b", "aa", "b"]
But I don't know how to approach this... please help me!
Use itertools.groupby.
If you want to join groups not belonging to a certain key
from itertools import groupby
def join_except_key(iterable, key='b'):
groups = groupby(iterable)
for k, group in groups:
if k != key:
yield ''.join(group) # more general: ''.join(map(str, group))
else:
yield from group
Demo:
>>> li1 = ["a", "a", "a", "a", "b", "b", "a", "a", "b", "c", "c", "b", "c", "c"]
>>> list(join_except_key(li1))
['aaaa', 'b', 'b', 'aa', 'b', 'cc', 'b', 'cc']
If you want to join groups belonging to a certain key
from itertools import groupby
def join_by_key(iterable, key='a'):
groups = groupby(iterable)
for k, group in groups:
if k == key:
yield ''.join(group) # more general: ''.join(map(str, group))
else:
yield from group
Demo:
>>> li1 = ["a", "a", "a", "a", "b", "b", "a", "a", "b", "c", "c", "b", "c", "c"]
>>> list(join_by_key(li1))
['aaaa', 'b', 'b', 'aa', 'b', 'c', 'c', 'b', 'c', 'c']
Details on what groupby produces (non generator approach for join_except_key)
>>> li1 = ["a", "a", "a", "a", "b", "b", "a", "a", "b", "c", "c", "b", "c", "c"]
>>> groups = [(k, list(group)) for k, group in groupby(li1)]
>>> groups
[('a', ['a', 'a', 'a', 'a']),
('b', ['b', 'b']),
('a', ['a', 'a']),
('b', ['b']),
('c', ['c', 'c']),
('b', ['b']),
('c', ['c', 'c'])]
>>>
>>> result = []
>>> for k, group in groups:
...: if k != 'b':
...: result.append(''.join(group))
...: else:
...: result.extend(group)
...:
>>> result
['aaaa', 'b', 'b', 'aa', 'b', 'cc', 'b', 'cc']
The list comprehension groups = [... in the second line was only needed for inspecting the elements of the grouping operation, it works fine with just groups = groupby(li1).
You can use itertools.groupby, dividing logic into 3 parts:
Group by equality to your separator string.
Construct an iterable of lists depending on the condition defined in groupby key.
Use itertools.chain.from_iterable to flatten your iterable of lists.
Here's a demonstration.
from itertools import chain, groupby
def sep_by_val(L, k='b'):
grouper = groupby(L, key=lambda x: x==k)
gen_of_lst = ([''.join(j)] if not i else list(j) for i, j in grouper)
return list(chain.from_iterable(gen_of_lst))
sep_by_val(li1) # ['aaaa', 'b', 'b', 'aa', 'b']
sep_by_val(li2) # ['aaa', 'b', 'a,', 'b', 'aa']
sep_by_val(li3) # ['b', 'b', 'aa', 'b']
Itertools and Yield from are great python constructs but challenging to master. Something simpler would go like so involving string shifting and splitting.
result = []
while len(li1) > 0:
split = ''.join(li1).partition('b')
before, part, after = split
if before:
result.extend( before.split() )
if part:
result.append(part)
li1 = after.split()
print(result)
Here is a function I wrote to perform this:
def Conbine(Li):
li=[]
li.append(Li[0])
Prev=Li[0]
for i in Li[1:]:
if not"b"in(i,Prev):li[-1]+=i
else:
Prev=i
li.append(i)
return li
Here is the result:
>>> Conbine(["a", "a", "a", "a", "b", "b", "a", "a", "b"])
['aaaa', 'b', 'b', 'aa', 'b']
>>> Conbine(["a", "a", "a", "b", "a,", "b", "a", "a"])
['aaa', 'b', 'a,', 'b', 'aa']
>>> Conbine(["b", "b", "a", "a", "b"])
['b', 'b', 'aa', 'b']
There are a lot of answers here already, but I hope this helped.
I don't get why all the answers look complicated for this. Did I miss something ?
li1 = ['a', 'a', 'a', 'b', 'b', 'a', 'a', 'b']
result = []
for e in li1:
if result and e != 'b' != result[-1]:
result[-1] += e
else:
result.append(e)
print(result)
Prints
['aaa', 'b', 'b', 'aa', 'b']
Keep it simple and stupid. Readability matters.
I'm late, but this is another option:
def join_in(lst, s):
res, append = [lst[0]], True
for i, e in enumerate(lst[1:]):
if res[-1][0] == s and e == s:
res[-1] += e
append = False
else: append = True
if append: res.append(e)
return res
Calling on the OP lists:
print (join_in(li1, 'a')) #=> ["aaaa", "b", "b", "aa", "b"]
print (join_in(li2, 'a')) #=> ["aaa", "b", "a", "b", "aa"]
print (join_in(li3, 'a')) #=> ["b", "b", "aa", "b"]
It is possible to call it on 'b':
print (join_in(join_in(li3, 'a'), 'b')) #=> ['bb', 'aa', 'b']

Categories

Resources