Related
I have a list like this
attach=['a','b','c','d','e','f','g','k']
I wanna pair each two elements that followed by each other:
lis2 = [('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'k')]
I did the following:
Category=[]
for i in range(len(attach)):
if i+1< len(attach):
Category.append(f'{attach[i]},{attach[i+1]}')
but then I have to remove half of rows because it also give 'b' ,'c' and so on. I thought maybe there is a better way
You can use zip() to achieve this as:
my_list = ['a','b','c','d','e','f','g','k']
new_list = list(zip(my_list[::2], my_list[1::2]))
where new_list will hold:
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'k')]
This will work to get only the pairs, i.e. if number of the elements in the list are odd, you'll loose the last element which is not as part of any pair.
If you want to preserve the last odd element from list as single element tuple in the final list, then you can use itertools.zip_longest() (in Python 3.x, or itertools.izip_longest() in Python 2.x) with list comprehension as:
from itertools import zip_longest # In Python 3.x
# from itertools import izip_longest ## In Python 2.x
my_list = ['a','b','c','d','e','f','g','h', 'k']
new_list = [(i, j) if j is not None else (i,) for i, j in zip_longest(my_list[::2], my_list[1::2])]
where new_list will hold:
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'h'), ('k',)]
# last odd number as single element in the tuple ^
You have to increment iterator i.e by i by 2 when moving forward
Category=[]
for i in range(0, len(attach), 2):
Category.append(f'{attach[i]},{attach[i+1]}')
Also, you don't need the if condition, if the len(list) is always even
lis2 = [(lis[i],lis[i+1]) for i in range(0,len(lis),2)]
lis2
You can use list comprehension
Let's assume there is a list of tuples:
for something in x.something()
print(something)
and it returns
('a', 'b')
('c', 'd')
('e', 'f')
('g', 'h')
('i', 'j')
And I have created two other lists containing certain elements from the x.something():
y = [('a', 'b'), ('c', 'd')]
z = [('e', 'f'), ('g', 'h')]
So I want to assign the tuples from x.something() to a new list based on y and z by
newlist = []
for something in x.something():
if something in 'y':
newlist.append('color1')
elif something in 'z':
newlist.append('color2')
else:
newlist.append('color3')
What I would like to have is the newlist looks like:
['color1', 'color1', 'color2', 'color2', 'color3']
But I've got
TypeError: 'in <string>' requires string as left operand, not tuple
What went wrong and how to fix it?
I think you want to get if something in y instead of if something in 'y' because they are two seperate lists, not strings:
newlist = []
for something in x.something():
if something in y:
newlist.append('color1')
elif something in z:
newlist.append('color2')
else:
newlist.append('color3')
You should remove the quotes from if something in 'y' because it assumes that you're checking if something is in the string 'y'. Same for z.
try this:
t = [('a', 'b'),
('c', 'd'),
('e', 'f'),
('g', 'h'),
('i', 'j')]
y = [('a', 'b'), ('c', 'd')]
z = [('e', 'f'), ('g', 'h')]
new_list = []
for x in t:
if x in y:
new_list.append('color1')
elif x in z:
new_list.append('color2')
else:
new_list.append('color3')
print(new_list)
output:
['color1', 'color1', 'color2', 'color2', 'color3']
I am using python regular expressions. I want all colon separated values in a line.
e.g.
input = 'a:b c:d e:f'
expected_output = [('a','b'), ('c', 'd'), ('e', 'f')]
But when I do
>>> re.findall('(.*)\s?:\s?(.*)','a:b c:d')
I get
[('a:b c', 'd')]
I have also tried
>>> re.findall('(.*)\s?:\s?(.*)[\s$]','a:b c:d')
[('a', 'b')]
The following code works for me:
inpt = 'a:b c:d e:f'
re.findall('(\S+):(\S+)',inpt)
Output:
[('a', 'b'), ('c', 'd'), ('e', 'f')]
Use split instead of regex, also avoid giving variable name like keywords
:
inpt = 'a:b c:d e:f'
k= [tuple(i.split(':')) for i in inpt.split()]
print(k)
# [('a', 'b'), ('c', 'd'), ('e', 'f')]
The easiest way using list comprehension and split :
[tuple(ele.split(':')) for ele in input.split(' ')]
#driver values :
IN : input = 'a:b c:d e:f'
OUT : [('a', 'b'), ('c', 'd'), ('e', 'f')]
You may use
list(map(lambda x: tuple(x.split(':')), input.split()))
where
input.split() is
>>> input.split()
['a:b', 'c:d', 'e:f']
lambda x: tuple(x.split(':')) is function to convert string to tuple 'a:b' => (a, b)
map applies above function to all list elements and returns a map object (in Python 3) and this is converted to list using list
Result
>>> list(map(lambda x: tuple(x.split(':')), input.split()))
[('a', 'b'), ('c', 'd'), ('e', 'f')]
I have found many algorithms and approaches that talk about finding the shortest path between 2 points , but i have this situation where the data is modeled as :
[(A,B),(C,D),(B,C),(D,E)...] # list of possible paths
If we suppose i need the path from A to E , the result should be:
(A,B)=>(B,C)=>(C,D)=>(D,E)
but i can't find a pythonic way to do this search.
The Pythonic way is to to use a module if one exists. As in this case, we know, networkx is there , we can write
Implementation
import networkx as nx
G = nx.Graph([('A','B'),('C','D'),('B','C'),('D','E')])
path = nx.shortest_path(G, 'A', 'E')
Output
zip(path, path[1:])
[('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'E')]
If you think of your points as vertices in a graph, your pairs as edges in that graph, then you can assign to edge graph edge a weight equal to the distance between your points.
Framed this way your problem is just the classic shortest path problem.
You asked for a Pythonic way to write it. The only advice I'd give is represent your graph as a dictionary, so that each key is a point, the returned values are a list of the other points directly reachable from that point. That will make traversing the graph faster. graph[C] -> [B, D] for your example.
Here is a solution using A*:
pip install pyformulas==0.2.8
import pyformulas as pf
transitions = [('A', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'F'), ('D', 'F'), ('F', 'D'), ('F', 'B'), ('D', 'E'), ('E', 'C')]
initial_state = ('A',)
def expansion_fn(state):
valid_transitions = [tn for tn in transitions if tn[0] == state[-1]]
step_costs = [1 for t in valid_transitions]
return valid_transitions, step_costs
def goal_fn(state):
return state[-1] == 'E'
path = pf.discrete_search(initial_state, expansion_fn, goal_fn) # A*
print(path)
Output:
[('A',), ('A', 'B'), ('B', 'C'), ('C', 'F'), ('F', 'D'), ('D', 'E')]
Questions: Can someone help me to figure out how to calculate cycles that have the maximum amount of pairs (three per cycle - see last example)?
This is what I want to do:
-> pair two users every cycle such that
- each user is only paired once with an other user in a given cycle
- each user is only paired once with every other user in all cycles
Real world:
You meet one new person from a list every week (week = cycle).
You never meet the same person again.
Every user is matched to someone else per week
This is my problem:
I'm able to create combinations of users and select pairs of users that never have met. However, sometimes I'm able to only match two pairs in a cycle instead of three. Therefore,
I'm searching for a way to create the optimal selections from a list of combinations.
1) I start with 6 users:
users = ["A","B","C","D","E","F"]
2) From this list, I create possible combinations:
x = itertools.combinations(users,2)
for i in x:
candidates.append(i)
This gives me:
. A,B A,C A,D A,E A,F
. . B,C B,D B,E B,F
. . . C,D C,E C,F
. . . . D,E D,F
. . . . . E,F
or
candidates = [('A', 'B'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('A', 'F'), ('B', 'C'),
('B', 'D'), ('B', 'E'), ('B', 'F'), ('C', 'D'), ('C', 'E'), ('C', 'F'),
('D', 'E'), ('D', 'F'), ('E', 'F')]
3) Now, I would like to select pairs from this list, such that a user (A to F) is only present once & all users are paired with someone in this cycle
Example:
cycle1 = ('A','B'),('C','D') ('E','F')
Next cycle, I want to find an other set of three pairs.
I calculated that with 6 users there should be 5 cycles with 3 pairs each:
Example:
cycle 1: AF BC DE
cycle 2: AB CD EF
cycle 3: AC BE DF
cycle 4: AE BD CF
cycle 5: AD BF CE
Can someone help me to figure out how to calculate cycles that have the maximum amount of pairs (three per cycle - see last example)?
Like Whatang mentioned in the comments your problem is in fact equivalent to that of creating a round-robin style tournament. This is a Python version of the algorithm mentioned on the Wikipedia page, see also this and this answer.
def schedule(users):
# first make copy or convert to list with length `n`
users = list(users)
n = len(users)
# add dummy for uneven number of participants
if n % 2:
users.append('_')
n += 1
cycles = []
for _ in range(n-1):
# "folding", `//` for integer division
c = zip(users[:n//2], reversed(users[n//2:]))
cycles.append(list(c))
# rotate, fixing user 0 in place
users.insert(1, users.pop())
return cycles
schedule(['A', 'B', 'C', 'D', 'E', 'F'])
For your example it produces the following:
[[('A', 'F'), ('B', 'E'), ('C', 'D')],
[('A', 'E'), ('F', 'D'), ('B', 'C')],
[('A', 'D'), ('E', 'C'), ('F', 'B')],
[('A', 'C'), ('D', 'B'), ('E', 'F')],
[('A', 'B'), ('C', 'F'), ('D', 'E')]]
Here's an itertools-based solution:
import itertools
def hasNoRepeats(matching):
flattenedList = list(itertools.chain.from_iterable(matching))
flattenedSet = set(flattenedList)
return len(flattenedSet) == len(flattenedList)
def getMatchings(users, groupSize=2):
# Get all possible pairings of users
pairings = list(itertools.combinations(users, groupSize))
# Get all possible groups of pairings of the correct size, then filter to eliminate groups of pairings where a user appears more than once
possibleMatchings = filter(hasNoRepeats, itertools.combinations(pairings, len(users)/groupSize))
# Select a series of the possible matchings, making sure no users are paired twice, to create a series of matching cycles.
cycles = [possibleMatchings.pop(0)]
for matching in possibleMatchings:
# pairingsToDate represents a flattened list of all pairs made in cycles so far
pairingsToDate = list(itertools.chain.from_iterable(cycles))
# The following checks to make sure there are no pairs in matching (the group of pairs being considered for this cycle) that have occurred in previous cycles (pairingsToDate)
if not any([pair in pairingsToDate for pair in matching]):
# Ok, 'matching' contains only pairs that have never occurred so far, so we'll add 'matching' as the next cycle
cycles.append(matching)
return cycles
# Demo:
users = ["A","B","C","D","E","F"]
matchings = getMatchings(users, groupSize=2)
for matching in matchings:
print matching
output:
(('A', 'B'), ('C', 'D'), ('E', 'F'))
(('A', 'C'), ('B', 'E'), ('D', 'F'))
(('A', 'D'), ('B', 'F'), ('C', 'E'))
(('A', 'E'), ('B', 'D'), ('C', 'F'))
(('A', 'F'), ('B', 'C'), ('D', 'E'))
Python 2.7. It's a little brute-forcey, but it gets the job done.
Ok this is pseudo code, but it should do the trick
while length(candidates) > length(users)/2 do
{
(pairs, candidates) = selectPairs(candidates, candidates)
if(length(pairs) == length(users)/2)
cycles.append(pairs)
}
selectPairs(ccand, cand)
{
if notEmpty(ccand) then
cpair = cand[0]
ncand = remove(cpair, cand)
nccand = removeOccurences(cpair, ncand)
(pairs, tmp) = selectPairs(nccand, ncand)
return (pairs.append(cpair), tmp)
else
return ([],cand)
}
where:
remove(x, xs) remove x from xs
removeOccurences(x, xs) remove every pair of xs containing at least one element of the pair `x
EDIT: the condition to stop the algorithm may need further thought ...