Lower elements in list given a certain position - python

I must lower letters in a list if the occupy a certain position given in a previous function I did. The function I must program is lower_words.
I'm having an issue: every time I lower an element the row is repeated.
I don't need to use the list "words" for this. Just left it there so you could understand better what the function does/must do. Can someone help me?
words= ["PATO", "GATO", "BOI", "CAO"]
grid1= ["PIGATOS",
"ANRBKFD",
"TMCAOXA",
"OOBBYQU",
"MACOUIV",
"EEJMIWL"]
positions_words_occupy = ((0, 0), (1, 0), (2, 0), (3, 0), (0, 2), (0, 3), (0, 4), (0, 5), (3, 2), (4, 3), (5, 4), (2, 2), (2, 3), (2, 4)) #these are the positions the words occupy. I have determined these positions with a previous function. first is the line, second the column
def lower_words(grid, positions_words_occupy):
new= []
for position in positions_words_occupy:
line= position[0]
column= position[1]
row= grid[line]
element= row[column]
new.append(row.replace(element, element.lower()))
return new
Expected output:
['pIgatoS', 'aNRBKFD', 'tMcaoXA', 'oObBYQU', 'MACoUIV', 'EEJMiWL']
Actual output:
['pIGATOS', 'aNRBKFD', 'tMCAOXA', 'ooBBYQU', 'PIgATOS', 'PIGaTOS', 'PIGAtOS', 'PIGAToS', 'OObbYQU', 'MACoUIV', 'EEJMiWL', 'TMcAOXA', 'TMCaOXa', 'TMCAoXA']
Changing the perspective, you can see it lowers the words I have in the list words:
['pIgatoS',
'aNRBKFD',
'tMcaoXA',
'oObBYQU',
'MACoUIV',
'EEJMiWL']

You are very close! You're actually appending to your new list new every time you replace a letter. That is why you are getting so many values in your list.
Another way you would run your code is to create a copy of grid1, and then replace each word every time you replace a letter. Here is a new function implementing these small changes:
def lower_words(grid, positions_words_occupy):
new = grid1.copy()
for position in positions_words_occupy:
line= position[0]
column= position[1]
row= new[line]
element= row[column]
#new.remove(row)
new_word = row[:column] + element.lower() + row[column+1:]
new[line] = new_word
return new
Output running lower_words(grid1, positions_words_occupy):
['pIgatoS', 'aNRBKFD', 'tMcaoXa', 'oObBYQU', 'MACoUIV', 'EEJMiWL']

I would first collect your grid positions in a collections.defaultdict or sets, then rebuild the strings with lowercase letters if their positions exist in these sets.
Demo:
from collections import defaultdict
grid1 = ["PIGATOS", "ANRBKFD", "TMCAOXA", "OOBBYQU", "MACOUIV", "EEJMIWL"]
positions_words_occupy = (
(0, 0),
(1, 0),
(2, 0),
(3, 0),
(0, 2),
(0, 3),
(0, 4),
(0, 5),
(3, 2),
(4, 3),
(5, 4),
(2, 2),
(2, 3),
(2, 4),
)
d = defaultdict(set)
for grid, pos in positions_words_occupy:
d[grid].add(pos)
result = []
for grid, pos in d.items():
result.append(
"".join(x.lower() if i in pos else x for i, x in enumerate(grid1[grid]))
)
print(result)
Output:
['pIgatoS', 'aNRBKFD', 'tMcaoXA', 'oObBYQU', 'MACoUIV', 'EEJMiWL']

Related

How to find the maximum per group in an rdd?

I'm using PySpark and I have an RDD that looks like this:
[
("Moviex", [(1, 100), (2, 20), (3, 50)]),
("MovieY", [(1, 100), (2, 250), (3, 100), (4, 120)]),
("MovieZ", [(1, 1000), (2, 250)]),
("MovieX", [(4, 50), (5, 10), (6, 0)]),
("MovieY", [(3, 0), (4, 260)]),
("MovieZ", [(5, 180)]),
]
The first element in the tuple represents the week number and the second element represents the number of viewers. I want to find the week with the most views for each movie, but ignoring the first week.
I've tried some things but nothing worked, for example:
stats.reduceByKey(max).collect()
returns:
[('MovieX', [(4, 50), (5, 10), (6, 0)]),
('MovieY', [(5, 180)]),
('MovieC', [(3, 0), (4, 260)])]
so the entire second set.
Also this:
stats.groupByKey().reduce(max)
which returns just this:
('MovieZ', <pyspark.resultiterable.ResultIterable at 0x558f75eeb0>)
How can I solve this?
If you want the most views per movie, ignoring the first week ... [('MovieA', 50), ('MovieC', 250), ('MovieB', 260)]
Then, you'll want your own map function rather than a reduce.
movie_stats = spark.sparkContext.parallelize([
("MovieA", [(1, 100), (2, 20), (3, "50")]),
("MovieC", [(1, 100), (2, "250"), (3, 100), (4, "120")]),
("MovieB", [(1, 1000), (2, 250)]),
("MovieA", [(4, 50), (5, "10"), (6, 0)]),
("MovieB", [(3, 0), (4, "260")]),
("MovieC", [(5, "180")]),
])
def get_views_after_first_week(v):
values = iter(v) # iterator of tuples, groupped by key
result = list()
for x in values:
result.extend([int(y[1]) for y in x if y[0] > 1])
return result
mapped = movie_stats.groupByKey().mapValues(get_views_after_first_week).mapValues(max)
mapped.collect()
to include the week number... [('MovieA', (3, 50)), ('MovieC', (2, 250)), ('MovieB', (4, 260))]
def get_max_weekly_views_after_first_week(v):
values = iter(v) # iterator of tuples, groupped by key
max_views = float('-inf')
max_week = None
for x in values:
for t in x:
week, views = t
views = int(views)
if week > 1 and views > max_views:
max_week = week
max_views = views
return (max_week, max_views, )
mapped = movie_stats.groupByKey().mapValues(get_max_weekly_views_after_first_week)
Some code is needed to convert the string into int, and apply a map function to 1) filter out week 1 data; 2) get the week with max view.
def helper(arr: list):
max_week = None
for sub_arr in arr:
for item in sub_arr:
if item[0] == 1:
continue
count = int(item[1])
if max_week is None or max_week[1] < count:
max_week = [item[0], count]
return max_week
movie_stats.groupByKey().map(lambda x: (x[0], helper(x[1]))).collect()

Order of For Loops with Python List Comprehension

In this answer, it is claimed that
The best way to remember this is that the order of for loop inside the list comprehension is based on the order in which they appear in traditional loop approach. Outer most loop comes first, and then the inner loops subsequently.
However, this answer,, and my own experiment below, seem to show the opposite - i.e, the inner loop coming first.
In my example, I want j to represent the row number and i to represent the column number. I want 5 rows and 4 columns What am I missing please?
board = [[(j, i) for i in range(4)] for j in range(5)]
# I believe the above comprehension is equivalent to the nested for loops below
# board = []
# for j in range(5):
# new_row = []
# for i in range(4):
# new_row.append((j,i))
# board.append(new_row)
for j in range(5):
for i in range(4):
print(board[j][i], end="")
print()
This is the correct way to get desired output:
board = [(j, i) for i in range(4) for j in range(5)]
Output:-
[(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3)]

Number of passengers. Error: list indices must be integers or slices, not list

So, I'm trying to sum the number of passenger at each stop.
The "stops" variable are the number of stops, and is conformed by a tuple which contains the in's and out's of passengers, example:
stops = [(in1, out1), (in2, out2), (in3, out3), (in4, out4)]
stops = [(10, 0), (4, 1), (3, 5), (3, 4), (5, 1), (1, 5), (5, 8), (4, 6), (2, 3)]
number_passenger_per_stop = []
for i in stops:
resta = stops[i][0] - stops[i][1]
number_passenger_per_stop.append(resta)
print(number_passenger_per_stop)
I can do the math like this outside the loop, but I don't understand why in the loop crashes:
stops[i][0] - stops[i][1]
i is not the list index, it's the list element itself. You don't need to write stops[i].
resta = i[0] - i[1]
Your code would be correct if you had written
for i in range(len(stops)):
You could also replace the entire thing with a list comprehension:
number_passenger_per_stop = [on - off for on, off in stops]
I just edited the for loop to adress each in the index in the list correctly, you needed to call each element in the list by its position, and not by its value:
stops = [(10, 0), (4, 1), (3, 5), (3, 4), (5, 1), (1, 5), (5, 8), (4, 6), (2, 3)]
number_passenger_per_stop = []
for i in range(len(stops)):
resta = stops[i][0] - stops[i][1]
number_passenger_per_stop.append(resta)
print(number_passenger_per_stop)
Output:
[10, 3, -2, -1, 4, -4, -3, -2, -1]

How to iterate over a dictionary of tuples

I have a list of tuples called possible_moves containing possible moves on a board in my game:
[(2, 1), (2, 2), (2, 3), (3, 1), (4, 5), (5, 2), (5, 3), (6, 0), (6, 2), (7, 1)]
Then, I have a dictionary that assigns a value to each cell on the game board:
{(0,0): 10000, (0,1): -3000, (0,2): 1000, (0,3): 800, etc.}
I want to iterate over all possible moves and find the move with the highest value.
my_value = 0
possible_moves = dict(possible_moves)
for move, value in moves_values:
if move in possible_moves and possible_moves[move] > my_value:
my_move = possible_moves[move]
my_value = value
return my_move
The problem is in the part for move, value, because it creates two integer indexes, but I want the index move to be a tuple.
IIUC, you don't even need the list of possible moves. The moves and their scores you care about are already contained in the dictionary.
>>> from operator import itemgetter
>>>
>>> scores = {(0,0): 10000, (0,1): -3000, (0,2): 1000, (0,3): 800}
>>> max_move, max_score = max(scores.items(), key=itemgetter(1))
>>>
>>> max_move
(0, 0)
>>> max_score
10000
edit: turns out I did not understand quite correctly. Assuming that the list of moves, let's call it possible_moves, contains the moves possible right now and that the dictionary scores contains the scores for all moves, even the impossible ones, you can issue:
max_score, max_move = max((scores[move], move) for move in possible_moves)
... or if you don't need the score:
max_move = max(possible_moves, key=scores.get)
You can use max with dict.get:
possible_moves = [(2, 1), (2, 2), (2, 3), (3, 1), (4, 5), (5, 2),
(5, 3), (6, 0), (6, 2), (7, 1), (0, 2), (0, 1)]
scores = {(0,0): 10000, (0,1): -3000, (0,2): 1000, (0,3): 800}
res = max(possible_moves, key=lambda x: scores.get(x, 0)) # (0, 2)
This assumes moves not found in your dictionary have a default score of 0. If you can guarantee that every move is included as a key in your scores dictionary, you can simplify somewhat:
res = max(possible_moves, key=scores.__getitem__)
Note the syntax [] is syntactic sugar for __getitem__: if the key isn't found you'll meet KeyError.
If d is a dict, iterator of d generates keys. d.items() generates key-value pairs. So:
for move, value in moves_values.items():
possibleMoves=[(2, 1), (2, 2), (2, 3), (3, 1), (4, 5), (5, 2),(0, 3),(5, 3), (6, 0), (6, 2), (7, 1),(0,2)]
movevalues={(0,0): 10000, (0,1): -3000, (0,2): 1000, (0,3): 800}
def func():
my_value=0
for i in range(len(possibleMoves)):
for k,v in movevalues.items():
if possibleMoves[i]==k and v>my_value:
my_value=v
return my_value
maxValue=func()
print(maxValue)

get indexes of elements from a zigzag configuration

"How could I get the indexes of elements in an n-row array configuration?
The length of a row should be given by a string of length l.
For example:
For a 2-row array configuration with l=7, the elements (X) will have indexes:
elements = [(0, 0), (0, 2), (0, 4), (0, 6), (1, 1), (1, 3), (1, 5), (1, 7)]
[[X - X - X - X],
[- X - X - X -]]
For a 3-rows array with l=8, the elements (X) will have indexes:
elements = [(0, 0), (0, 4), (0, 8), (1, 1), (1, 3), (1, 5), (1, 7), (2, 2), (2, 6)]
[[X - - - X - - - X],
[- X - X - X - X -],
[- - X - - - X - -]]
The idea is to extended to higher row numbers. Is there an "analytical" way of getting those indexes?
Thanks in advance.
P.S.: By "analytical" I mean an equation or something that I could code
this is my first shot at your problem:
def grid(width, depth):
assert depth % 2 == 0
height = depth//2 + 1
lines = []
for y in range(height):
line = ''.join('X' if ((i+y) % depth == 0 or (i-y) % depth == 0)
else '-' for i in range(width))
lines.append(line)
return '\n'.join(lines)
the depth is the parameter that defines how far the Xs are spaces on fhe first line (the name is poorly chosen); the width is how many characters should be displayed per line.
this will only work for even depths.
with outputs
-> print(grid(width=10, depth=2))
X-X-X-X-X-
-X-X-X-X-X
-> print(grid(width=10, depth=4))
X---X---X-
-X-X-X-X-X
--X---X---
-> print(grid(width=15, depth=6))
X-----X-----X--
-X---X-X---X-X-
--X-X---X-X---X
---X-----X-----
this was mostly trial & error so there is not much to explain...
if you prefer your elements representation - here is what you can do:
def grid_elements(width, depth):
assert depth % 2 == 0
height = depth//2 + 1
elements = []
for y in range(height):
elements.extend((y, i) for i in range(width)
if ((i+y) % depth == 0 or (i-y) % depth == 0))
return elements
this creates the results:
-> print(grid_elements(width=10, depth=2))
[(0, 0), (0, 2), (0, 4), (0, 6), (0, 8), (1, 1), (1, 3), (1, 5), (1, 7), (1, 9)]
-> print(grid_elements(width=10, depth=4))
[(0, 0), (0, 4), (0, 8), (1, 1), (1, 3), (1, 5), (1, 7), (1, 9), (2, 2), (2, 6)]
-> print(grid_elements(width=15, depth=6))
[(0, 0), (0, 6), (0, 12), (1, 1), (1, 5), (1, 7), (1, 11), (1, 13), (2, 2),
(2, 4), (2, 8), (2, 10), (2, 14), (3, 3), (3, 9)]
This is a example of code that can do this.
import numpy as np
nb_row = 3; nb_column = 10;
separator_element = '-'; element = 'X';
#Initialise the size of the table
table = np.chararray((nb_row, nb_column), itemsize=1);
table[:] = separator_element; #By default, all have the separator element.
#Loop over each column: First column have element at first row. The element
#will after decrease and wrap around the nb of row.
#When at the bottom, switch to go up. At top, switch to go down.
position_element = 0; go_down = 1;
for no_column in xrange(0,nb_column):
table[position_element,no_column] = element;
#Case when go down.
if go_down == 1:
position_element = (position_element+1) % (nb_row);
go_down = (position_element != (nb_row-1)); #Go up after go down.
#Case when go up;
else:
position_element = (position_element-1) % (nb_row);
go_down = (position_element == 0); #Go up after go down.
#end
#end
print(table)
#[['X' '-' '-' '-' 'X' '-' '-' '-' 'X' '-']
#['-' 'X' '-' 'X' '-' 'X' '-' 'X' '-' 'X']
#['-' '-' 'X' '-' '-' '-' 'X' '-' '-' '-']]
We can use itertools.groupby here to create a dictionary that has the
sublist indexes of interest as values and index of sublists as keys {0: [0, 2, 4, 6], 1: [1, 3, 5, 7]}, We can then use this on list that is generated using n = 7. From there we can modify the sublist using the indexes that are values for the corresponding sublist index in our keys.
from itertools import groupby
elements = [(0, 0), (0, 2), (0, 4), (0, 6), (1, 1), (1, 3), (1, 5), (1, 7)]
n = 7
d = {}
for k, g in groupby(elements, key=lambda x: x[0]):
d[k] = [i[1] for i in g]
lst = [['-']*n for i in d]
for k in d:
for i, v in enumerate(lst[k]):
if i in d[k]:
lst[k][i] = 'X'
lst[k] = ' '.join(lst[k])
for i in lst:
print(i)
# X - X - X - X
# - X - X - X -

Categories

Resources