Python - Invert list order - python-3.x

I want to invert list order without changing the values.
The original list is the following:
[15, 15, 10, 8, 73, 1]
While the resulting expecting list is:
[10, 8, 15, 15, 1, 73]
The example has been taken from a real data handling problem from a more complex pandas data frame.
I proposed a list problem only to simplify the issue. So, it can also be a pandas function.

zlist = int(len(list)/2)
for i in range(0, zlist):
a, b = list.index(sorted(list, reverse=True)[i]), list.index(sorted(list,reverse=False)[i])
list[b], list[a] = list[a], list[b]

Related

Value Error when trying to create a dictionary with lists as values

I am having issues creating a dictionary that assigns a list of multiple values to each key. Currently the data is in a list of list of 2 items:Category and Value, for example:
sample_data = [["January", 9],["Februrary", 10], ["June", 12], ["March", 15], ["January", 10],["June", 14], ["March", 16]]
It has to be transformed into a dicitonary like this:
d = {"January" : [9,10], "February":[10], "June":[12,14], "March": [15,16]}
This is my current code:
d = defaultdict(list)
for category, value in sample_data:
d[category].append(value)
This works for small samples but with very large samples of data it raises a ValueError saying too much values to unpack. Is there any way I could improve on this code or is there another way of doing this?
So, the setdefault method creates a list as the value for a key.
d = defaultdict(list)
for category, value in sample_data:
d.setdefault(category, []).append(value)
Output:
defaultdict(<class 'list'>, {'January': [9, 10], 'Februrary': [10], 'June': [12, 14], 'March': [15, 16]})
Note: I do not have a larger sample set to work with but the setdefault() method could possibly help out with that.
One way to solve this is prob. change the code to accept more than one values. This is just a wild guess - could it be something in your data (eg. value) problem - eg. one particular month has 2+ more data points showing all at once.
Note - *value means that it can take multiple values (more than one)
Without the * before value, it can only take one number at a time. That is why you got the error "Too many values to unpack..."
Because the sample data is not complete enough to show the exact error point, there's prob. other issue with data. But it could help you eliminate the earlier "error"...or narrow down to it.
data = [["January", 9],["Februrary", 10], ["June", 12],
["March", 15], ["January", 10],["June", 14], ["March", 16],
['April', 20, 21, 22]] # <--- add April & 3 values (to handle the earlier error)
from collections import defaultdict
# d = {"January" : [9,10], "February":[10], "June":[12,14],
# "March": [15,16]}
# This is my current code:
dc = defaultdict(list)
for category, *value in data: # *value to accept multiple values
dc[category].append(value)
print(dc)
output:
defaultdict(<class 'list'>, {'January': [[9], [10]], 'Februrary': [[10]], 'June': [[12], [14]], 'March': [[15], [16]], 'April': [[20, 21, 22]]})

Python: unique weighted random values

From this thread I found out that I can use an approach with the random.choices for my needs:
class Weights:
ITEM = {
'a': 0.5,
'b': 0.4,
'c': 0.3,
'd': 0.2,
'e': 0.1
}
import random
slot_1 = random.choices(population=list(Weights.ITEM.keys()), weights=list(Weights.ITEM.values()), k=1)[0]
slot_2 = ...?
slot_3 = ...?
Is it possible for me to get an array with the k=3 that will have "unique" results (probably [a,b,c]) or somehow to exclude any previously selected value from the next call (with k=1)?
For example lets say slot_1 got "b" and slot_2 will get a random from the list of everything else without the "b" value.
This step can be sensitive to the performance and I think that creating new arrays each time is not a good idea.
Maybe there is something except random.choices that can be applied in this case.
You could take all the samples all at once using numpy's random.choice with the replace = False option (assuming the weights are just renormalized between steps,) and store them using multiple assignment, to get it into one line of code.
import numpy as np
slot_1, slot_2, slot_3 = np.random.choice(list(Weights.ITEM.keys()), size = 3, replace=False, p=list(Weights.ITEM.values()))
More generally, you could have a function that generated arbitrary length subsamples (k is length, n is number of samples):
def a(n,k,values,weights):
a = np.split(np.random.choice(values, size = n*k,replace=False, p=weights), n)
return [list(sublist) for sublist in a]
>>> a(3,5, range(100), [.01]*100)
[[39, 34, 27, 91, 88], [19, 98, 62, 55, 38], [37, 22, 54, 11, 84]]

Removing duplicates in a list (Python 3.8.3)

I want to just understand why the below code is not very good at removing duplicates:
numbers = [4, 4, 4, 10, 10, 10, 20, 20, 2]
for duplicates in numbers:
numbers.count(duplicates)
if numbers.count(duplicates) > 1:
numbers.remove(duplicates)
numbers.sort()
print(numbers)
The final output is: [2, 4, 10, 10, 20]
This could just be a fundamental error in my understanding of the methods used, but I would like to know what is wrong here.
PS. I'm aware I could do an easier version of this code posted below, but I am just curious.
Easier version of above code:
numbers = [4, 4, 4, 10, 10, 10, 20, 20, 2]
unique_list = []
for duplicates_check in numbers:
if duplicates_check not in unique_list:
unique_list.append(duplicates_check)
unique_list.sort()
print(unique_list)
Thanks in advance for your help!
Modifying the list as you're iterating over it is a bad idea (since the loop gets "confused" about which index it's on).
Since you're sorting the numbers in the end, you apparently don't care about the order.
Just do
numbers = sorted(set(numbers))
– that is, convert the numbers to a set; sets can only contain each element once, then use sorted() to sort the set into a new list.
If you would care about the order,
def unique_in_order(iterable):
seen = set()
for item in iterable:
if item not in seen:
seen.add(item)
yield item
numbers = list(unique_in_order(iterable))
is your recipe.

Python: Is it possible to do the same using reduce()?

This is part of an assaigment so it needs to be donde using the reduce function (or filter although I don't see it), hence I'd like to know if it's possible.
I have two dicts:
takeOff_Airport = {'LPPD': 4, 'DAAS': 1, 'EDDH': 16, 'LFLL': 17, 'LFPO': 30}
landing_Airport = {'LFPO': 12, 'LPPD': 7, 'UUEE': 11, 'DAAS': 7, 'LFSL': 1}
After applying the follwing code:
airports = (sorted([[k, [v1+landing_Airport[k], v1,landing_Airport[k]]] for k,
v1 in takeOff_Airport.items() if k in landing_Airport],
key=lambda kv:kv[1], reverse=True))
I get the expected result:
airports: [['LFPO', 42, 30, 12], ['LPPD', 11, 4, 7], ['DAAS', 8, 1, 7]]
What 'airports' is printing is a list of lists with the common airport names in both dictionaries (landing and takeoff) and adding the sum of each dict value as well as each of the dictionaries [key:value ].
Is it possible to implement the above using some lambda expression in a reduce function? Maybe in a filter?
It is definitely possible.
The lambda takes as arguments the array x which aggregates the result and the key into one of the airports dictionaries (takeOff_Airport in my example).
If the key exists in the other airport dictionary, then the element formed by [key, sum of each dict value, takeOff value, landing value] is added to the array x. Else, array x is left unchanged.
Pass the lambda into the reduce function, setting the initial value of x to an empty array and it will generate the desired result.
airports = reduce(lambda x, key : x + [[key, takeOff_Airport[key] + landing_Airport[key], takeOff_Airport[key], landing_Airport[key]]] if key in landing_Airport else x, takeOff_Airport, [])
Result:
>>> airports
[['LPPD', 11, 4, 7], ['DAAS', 8, 1, 7], ['LFPO', 42, 30, 12]]

Python – next function – list comprehension

I am trying to understand why code written in a compact style works and the “same code-ish” in a more verbose style does not work.
This code yields the correct answer:
which_days = [5, 12, 19, 26]
buy_day = next((i for i in which_days if i in range(13, 20)), None)
print(buy_day)
>>> 19 <- correct answer
This code does yields a pair of brackets:
which_days = [5, 12, 19, 26]
buy_day = []
for i in which_days:
if i in range((13, 20), None):
next(which_days)
print(buy_day)
>>>[] <- This is a pair of brackets
Here is the long form of the correct code:
which_days = [5, 12, 19, 26]
def _tmp_variable():
for i in which_days:
if i in range(13, 20):
yield i
buy_day = next(_tmp_variable(), None)
del _tmp_variable
The incorrect code does not work because it is doing something entirely different (not to mention illegal; you can't call next() on a list in Python 3).
Here's a different long form, which builds the entire list eagerly instead of lazily yielding one element at a time:
which_days = [5, 12, 19, 26]
_tmp_variable = []
for i in which_days:
if i in range(13, 20):
_tmp_variable.append(i)
buy_day = _tmp_variable[0] if _tmp_variable else None
del _tmp_variable
As you can see, this is wasteful because it builds up an entire list and then only uses the first element. Fortunately, the short version of your code does not do this.

Resources