2d arrays and how to populate them with one dimensional arrays - python

This is my code:
def SetUpScores():
scoreBoard= []
names = ["George", "Paul", "John", "Ringo", "Bryan"]
userScores = [17, 19, 23, 25, 35]
for i in range(0,5):
scoreBoard.append([])
for j in range(0,2):
scoreBoard[i].append(names[i])
scoreBoard[i][1]= userScores[i]
I'm basically trying to create a two dimensional array that holds the name and the userScore, I have looked this up alot and so far I keep getting the error of list assignment index out of range or 'list' cannot be called.
If i remove the last line from my code i.e:
def SetUpScores():
scoreBoard= []
names = ["George", "Paul", "John", "Ringo", "Bryan"]
userScores = [17, 19, 23, 25, 35]
for i in range(0,5):
scoreBoard.append([])
for j in range(0,2):
scoreBoard[i].append(names[i])
I get
[['George', 'George'], ['Paul', 'Paul'], ['John', 'John'], ['Ringo', 'Ringo'], ['Bryan', 'Bryan']] without any errors (this is just to test if the array was made).
I would like to make something like:
[['George', 17], ['Paul', 19], ['John', 23], ['Ringo', 25], ['Bryan', 35]]
Any help would be appreciated, thank you!

With the line scoreBoard[i].append(names[i]), you add a single element, not a list. So, the next line scoreBoard[i][1]= userScores[i] causes an error, because it refers to the second element of names[i], which is just a string.
The most compact way to do what you want would be
for name, score in zip(names, userScores):
scoreBoard.append([name, score])

names = ["George", "Paul", "John", "Ringo", "Bryan"]
userScores = [17, 19, 23, 25, 35]
L3 =[]
for i in range(0, len(L1)):
L3.append([L1[i], L2[i]])
print(L3)
Output:
[[17, 'George'], [19, 'Paul'], [23, 'John'], [25, 'Ringo'], [35, 'Bryan']]

Related

Python - 2D list - find duplicates in one column and sum values in another column

I have a 2D list that contains soccer player names, the number of times they scored a goal, and the number of times they attempted a shot on goal, respectively.
player_stats = [['Adam', 5, 10], ['Kyle', 12, 18], ['Jo', 20, 35], ['Adam', 15, 20], ['Charlie', 31, 58], ['Jo', 6, 14], ['Adam', 10, 15]]
From this list, I'm trying to return another list that shows only one instance of each player with their respective total goals and total attempts on goal, like so:
player_stats_totals = [['Adam', 30, 45], ['Kyle', 12, 18], ['Jo', 26, 49], ['Charlie', 31, 58]]
After searching on Stack Overflow I was able to learn (from this thread) how to return the indexes of the duplicate players
x = [player_stats[i][0] for i in range (len(player_stats))]
for i in range (len(x)):
if (x[i] in x[:i]) or (x[i] in x[i+1:]): print (x[i], i)
but got stuck on how to proceed thereafter and if indeed this method is strictly relevant for what I need(?)
What's the most efficient way to return the desired list of totals?
Use a dictionary to accumulate the values for a given player:
player_stats = [['Adam', 5, 10], ['Kyle', 12, 18], ['Jo', 20, 35], ['Adam', 15, 20], ['Charlie', 31, 58], ['Jo', 6, 14], ['Adam', 10, 15]]
lookup = {}
for player, first, second in player_stats:
# if the player has not been seen add a new list with 0, 0
if player not in lookup:
lookup[player] = [0, 0]
# get the accumulated total so far
first_total, second_total = lookup[player]
# add the current values to the accumulated total, and update the values
lookup[player] = [first_total + first, second_total + second]
# create the output in the expected format
res = [[player, first, second] for player, (first, second) in lookup.items()]
print(res)
Output
[['Adam', 30, 45], ['Kyle', 12, 18], ['Jo', 26, 49], ['Charlie', 31, 58]]
A more advanced, and pythonic, version is to use a collections.defaultdict:
from collections import defaultdict
player_stats = [['Adam', 5, 10], ['Kyle', 12, 18], ['Jo', 20, 35],
['Adam', 15, 20], ['Charlie', 31, 58], ['Jo', 6, 14], ['Adam', 10, 15]]
lookup = defaultdict(lambda: [0, 0])
for player, first, second in player_stats:
# get the accumulated total so far
first_total, second_total = lookup[player]
# add the current values to the accumulated total, and update the values
lookup[player] = [first_total + first, second_total + second]
# create the output in the expected format
res = [[player, first, second] for player, (first, second) in lookup.items()]
print(res)
This approach has the advantage of skipping the initialisation. Both has approaches are O(n).
Notes
The expression:
res = [[player, first, second] for player, (first, second) in lookup.items()]
is a list comprehension, equivalent to the following for loop:
res = []
for player, (first, second) in lookup.items():
res.append([player, first, second])
Additionally, read this for understanding unpacking.
What you want to do is use a dictionary where the key is the player name and the value is a list containing [goals, shots]. Constructing it would look like this:
all_games_stats = {}
for stat in player_stats:
player, goals, shots = stat
if player not in all_games_stats:
all_games_stats[player] = [goals, shots]
else:
stat_list = all_games_stats[player]
stat_list[0] += goals
stat_list[1] += shots
Then, if you want to represent the players and their stats as a list, you would do:
list(all_games_stats.items())
You can convert the list to a dictionary. (It can always be changed back once done) This works:
player_stats = [['Adam', 5, 10], ['Kyle', 12, 18], ['Jo',
20, 35], ['Adam', 15, 20], ['Charlie', 31, 58], ['Jo', 6,
14], ['Adam', 10, 15]]
new_stats = {}
for item in player_stats:
if not item[0] in new_stats:
new_stats[item[0]] = [item[1],item[2]]
else:
new_stats[item[0]][0] += item[1]
new_stats[item[0]][1] += item[2]
print(new_stats)
I might as well submit something, too. Here's yet another method with some list comprehension worked in:
# Unique values to new dictionary with goal and shots on goal default entries
agg_stats = dict.fromkeys(set([p[0] for p in player_stats]), [0, 0])
# Iterate over the player stats list
for player in player_stats:
# Set entry to sum of current and next stats values for the corresponding player.
agg_stats[player[0]] = [sum([agg_stats.get(player[0])[i], stat]) for i, stat in enumerate(player[1:])]
Yet another way, storing the whole triples (including the name) in the dict and updating them:
stats = {}
for name, goals, attempts in player_stats:
entry = stats.setdefault(name, [name, 0, 0])
entry[1] += goals
entry[2] += attempts
player_stats_totals = list(stats.values())
And for fun a solution with complex numbers, which makes adding nice but requires annoying conversion back:
from collections import defaultdict
tmp = defaultdict(complex)
for name, *stats in player_stats:
tmp[name] += complex(*stats)
player_stats_totals = [[name, int(stats.real), int(stats.imag)]
for name, stats in tmp.items()]

Python: Finding strings in a list containing an interval and replacing this interval by every number in it

I'm wondering if there's any beautiful/clean way of doing what I'm trying to :).
(I'm sure there is)
So My function receives a list of strings that can either contains strings in 2 format:
"12,13,14,15" or "12 to 15"
The goal is to parse the second type and replace the "to" by the numbers in the interval.
Delimiters between numbers doesn't matter, a regex will do the job after.
Here is pseudo code and an ugly implementation
The idea is to replace "to" in the list by the numbers in the interval so that I can easily parse numbers with a regex afterwards
# The list is really inconsistent, separators may change and it's hand filled so some comments like in the last example might be present
l = ["12,13,14,15",
"12 to 18",
"10,21,22 to 42",
"14,48,52",
"12,14,22;45 and also 24 to 32"
]
def process_list(l):
for x in l:
if "to" in x:
# Find the 2 numbers around the to and replace the "to" by ",".join(list([interval of number]))
final_list = numero_regex.findall(num)
return final_list
I think you don't need regex:
def process_list(l):
final_list = []
for s in l:
l2 = []
for n in s.split(','):
params = n.split(' to ')
nums = list(range(int(params[0]), int(params[-1])+1))
l2.extend(nums)
final_list.append(l2)
return final_list
Output:
>>> process_list(l)
[[12, 13, 14, 15],
[12, 13, 14, 15, 16, 17, 18],
[10, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42],
[14, 48, 52]]
Update:
I wanted an output for this case like this ["12,21,32;14, and the 12,13,14,15,[...],40"]. Which I can really easily parse with a regex
If you just want to replace 'number1 to number2', you can do:
def process_list(l):
def to_range(m):
return ','.join([str(i) for i in range(int(m.group('start')),
int(m.group('stop'))+1)])
return [re.sub(pat, to_range, s) for s in l]
Output:
# l = ["12,21,18 to 20;32;14, and the 12 to 16"]
>>> process_list(l)
['12,21,18,19,20;32;14, and the 12,13,14,15,16']
Here is one solution:
from itertools import chain
def split(s):
return list(chain(*(list(range(*list(map(int, x.split(' to ')))))+[int(x.split(' to ')[1])]
if ' to ' in x else
[int(x)]
for x in s.split(',')
)))
[split(e) for e in l]
output:
[[12, 13, 14, 15],
[12, 13, 14, 15, 16, 17, 18],
[10, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42],
[14, 48, 52]]
edit: I adapted the above solution to be used with regexex:
from itertools import chain
def split(s):
regex = re.compile('(\d+\s*to\s*\d+|\d+)')
return list(chain(*([int(x)] if x.isnumeric() else
list(range(*map(int, re.split('\s+to\s+', x))))
+[int(re.split('\s+to\s+', x)[-1])]
for x in regex.findall(s)
)))

Get averages of lists in a dictionary

I have a dictionary that has lists inside it eg:
dict = {'Monday,': [10, 20], 'Tuesday,': [20, 20], 'Wednesday,': [30, 40], 'Thursday,': [5, 25], 'Friday,': [100, 20], 'Saturday,': [40, 10], 'Sunday,': [35, 25]}
And I need to get an average of each day eg:
{'Monday,': [15.00], 'Tuesday,': [20.00], 'Wednesday,': [35.00], 'Thursday,': [15.00], 'Friday,': [60.00], 'Saturday,': [25.00], 'Sunday,': [30.00]}
I have this at the moment but it is not working
average = sum(dict) / len(dict)
first, please don't name your dict as dict because that's a name of a python built-in
This will work for you:
average = {key: sum(val)/len(val) for key, val in dict.items()}
but beware that you assumed all lists are of numbers and no list is empty.

Python: Inserting an element into an array of different size

I have a numpy array like this:
[[[287 26]]
[[286 27]]
[[285 27]]
...
[[290 27]]
[[289 26]]
[[288 26]]]
and I would like to insert an integer and make it an array like
[
[287 26 integer]
[286 27 integer]
.......]
however, since the first array has different size than what I want at the end, simply using insert() function did not work for me.
Is there a work around?
Thanks in advance.
EDIT: So the closest I came so far is the following:
outline_poses = [] # final array
for cnt in cnts: # loop over each element
outline_poses.append(cnt[0])
outline_poses.append(SENSOR_MEASURED_HEIGHT) #Append this integer
Output:
[array([287, 26], dtype=int32), 60, array([286, 27], dtype=int32), 60,....]
How can I organize this array and make it look like [287, 26, 60],...?
If I understand your problem right, you could use a list comprehension.
newList = np.array([np.append(x[0],integer) for x in myList])
This is a three-dimensional list you have right here...
>>> myList = [[[287, 26]],
[[286, 27]],
[[285, 27]],
[[290, 27]],
[[289, 26]],
[[288, 26]]]
...so you'll need to access your list with two-levels of depth before inserting or appending elements into the deepest lists.
>>> myList[0][0].append(42)
>>> myList[5][0].append(42)
>>> myList
[[[287, 26, 42]],
[[286, 27]],
[[285, 27]],
[[290, 27]],
[[289, 26]],
[[288, 26, 42]]]
What happens when you insert or append elements with shallower depths? 🤔
Appending at Depth 0
>>> myList.append('Catapult')
>>> myList
[[[287, 26, 42], 'Trebuchet'],
[[286, 27]],
[[285, 27]],
[[290, 27]],
[[289, 26]],
[[288, 26, 42]],
'Catapult']
Appending at Depth 1
>>> myList[0].append('Trebuchet')
>>> myList[3].append('Treebuchet')
>>> myList
[[[287, 26, 42], 'Trebuchet'],
[[286, 27]],
[[285, 27]],
[[290, 27], 'Treebuchet'],
[[289, 26]],
[[288, 26, 42]]]
If I'm correct you are trying to insert an integer to all inner lists. You can use numpy concatenate method to achieve this.
integer_to_insert = 6
original_array = np.array([[[290, 27]],[[289, 26]],[[288, 26]]])
concat_integer = np.array([integer_to_insert]* original_array.shape[0]).reshape(original_array.shape[0], 1,1)
# This is correct if you are inserting the same integer to all lists. But as long as length of this array is equal to length of original list this array can be replaced.
concatenated = np.concatenate([original_array, concat_integer], 2)
print(concatenated)
# array([[[290, 27, 6]],
# [[289, 26, 6]],
# [[288, 26, 6]]])

Converting generators to lists overwrites the values in Python [duplicate]

This question already has an answer here:
Python: How to append generator iteration values to a list
(1 answer)
Closed 4 years ago.
I have this piece of code in Python 3.5 that implements generators for batching of potentially large buffered data:
def batched_data(data, size=20):
batch = []
for d in data:
batch.append(d)
if len(batch) == size:
yield batch
batch.clear()
yield batch
def buffered_data(data, bufminsize=10, bufmaxsize=20):
diter = iter(data)
buffer = collections.deque(next(diter) for _ in range(bufmaxsize))
while buffer:
yield buffer.popleft()
if len(buffer) < bufminsize:
buffer.extend(
next(diter) for _ in range(bufmaxsize - len(buffer)))
def batched_buffered_data(data, bufsize=100, batch=20):
yield from batched_data(
buffered_data(data, bufmaxsize=bufsize,
bufminsize=bufsize - batch),
size=batch)
Now, when I initialize my generator and traverse the batches in a for-loop, everything's fine:
In [351]: gen = batched_buffered_data(range(27), bufsize=10, batch=7)
In [352]: for g in gen:
...: print(g)
...:
[0, 1, 2, 3, 4, 5, 6]
[7, 8, 9, 10, 11, 12, 13]
[14, 15, 16, 17, 18, 19, 20]
[21, 22, 23, 24, 25, 26]
However, when I try to use list comprehension or conversion using list, this happens:
In [353]: gen = batched_buffered_data(range(27), bufsize=10, batch=7)
In [354]: list(gen)
Out[354]:
[[21, 22, 23, 24, 25, 26],
[21, 22, 23, 24, 25, 26],
[21, 22, 23, 24, 25, 26],
[21, 22, 23, 24, 25, 26]]
I am really puzzled about this. There must be some sort of mutable elements involved, but I really don't know what's behind this behavior.
Changing batch.clear() to batch = [] should fix it. The problem is the batch list, after clear(), is still a reference to the one original list. The yield seems to work because it's printing the one list as it appears at the time before mutating the elements on the next yield. Setting it to a new list on each yield breaks the reference to the previous iteration, so there's no aliasing going on.
If you're still confused, check out this example using your original code with .clear():
result = []
gen = batched_buffered_data(range(27), bufsize=10, batch=7)
for g in gen:
result.append(g)
[print(x) for x in result]
Output:
[21, 22, 23, 24, 25, 26]
[21, 22, 23, 24, 25, 26]
[21, 22, 23, 24, 25, 26]
[21, 22, 23, 24, 25, 26]
You should change batch.clear() to batch = [].
That is because .clear() clear the list and all the variables that point that list become [], on other way batch = [] just create a new list and assign it to batch

Categories

Resources