How to create a list of named lists in Python? - python-3.x

I need to create a list of named lists, so that I can iterate over them. This works, but I'm wondering if there isn't a better way.
terms = []; usedfors = []; broaders = []; narrowers = []
termlist = [terms, usedfors, broaders, narrowers]
The reason for doing it this way is that I have a function do_some_list_operation(l) and I want to do something like
for i in termlist:
do_some_list_operation(i)
rather than
do_some_list_operation(terms)
do_some_list_operation(usedfors)
do_some_list_operation(broaders)
do_some_list_operation(narrowers)
I've been searching for 'how to build list of named lists' to no avail.

The way you are doing it is fine but be aware that you are creating a list of lists when you defined termlist. If you want to have the name of the lists then termlist should be:
termlist = ["terms","usedfors", "broaders", "narrowers"]
Now if you want to use these strings as list names you can use globals() or locals() e.g.:
terms = [1]; usedfors = [2]; broaders = [3,4,8]; narrowers = [5]
termlist = ["terms","usedfors", "broaders", "narrowers"]
for i in termlist:
print(sum(locals()[i]))
output:
1
2
15
5

While I like the answer by #jayvee and upvoted it, I think a more conventional answer would be one based on using a dictionary as first suggested by #msaw328.
Starting with our new data structure:
term_data = {
"terms": [1],
"usedfors": [2],
"broaders": [3, 4, 5],
"narrowers": [6, 7]
}
Perhaps like:
for term in term_data:
print(f"{term} sums to {sum(term_data[term])}")
or even
for term, term_value in term_data.items():
print(f"{term} sums to {sum(term_value)}")
Both should give you:
terms sums to 1
usedfors sums to 2
broaders sums to 12
narrowers sums to 13

Related

How to find match between two 2D lists in Python?

Lets say I have two 2D lists like this:
list1 = [ ['A', 5], ['X', 7], ['P', 3]]
list2 = [ ['B', 9], ['C', 5], ['A', 3]]
I want to compare these two lists and find where the 2nd item matches between the two lists e.g here we can see that numbers 5 and 3 appear in both lists. The first item is actually not relevant in comparison.
How do I compare the lists and copy those values that appear in 2nd column of both lists? Using 'x in list' does not work since these are 2D lists. Do I create another copy of the lists with just the 2nd column copied across?
It is possible that this can be done using list comprehension but I am not sure about it so far.
There might be a duplicate for this but I have not found it yet.
The pursuit of one-liners is a futile exercise. They aren't always more efficient than the regular loopy way, and almost always less readable when you're writing anything more complicated than one or two nested loops. So let's get a multi-line solution first. Once we have a working solution, we can try to convert it to a one-liner.
Now the solution you shared in the comments works, but it doesn't handle duplicate elements and also is O(n^2) because it contains a nested loop. https://wiki.python.org/moin/TimeComplexity
list_common = [x[1] for x in list1 for y in list2 if x[1] == y[1]]
A few key things to remember:
A single loop O(n) is better than a nested loop O(n^2).
Membership lookup in a set O(1) is much quicker than lookup in a list O(n).
Sets also get rid of duplicates for you.
Python includes set operations like union, intersection, etc.
Let's code something using these points:
# Create a set containing all numbers from list1
set1 = set(x[1] for x in list1)
# Create a set containing all numbers from list2
set2 = set(x[1] for x in list2)
# Intersection contains numbers in both sets
intersection = set1.intersection(set2)
# If you want, convert this to a list
list_common = list(intersection)
Now, to convert this to a one-liner:
list_common = list(set(x[1] for x in list1).intersection(x[1] for x in list2))
We don't need to explicitly convert x[1] for x in list2 to a set because the set.intersection() function takes generator expressions and internally handles the conversion to a set.
This gives you the result in O(n) time, and also gets rid of duplicates in the process.

How to subtract adjacent items in list with unknown length (python)?

Provided with a list of lists. Here's an example myList =[[70,83,90],[19,25,30]], return a list of lists which contains the difference between the elements. An example of the result would be[[13,7],[6,5]]. The absolute value of (70-83), (83-90), (19-25), and (25-30) is what is returned. I'm not sure how to iterate through the list to subtract adjacent elements without already knowing the length of the list. So far I have just separated the list of lists into two separate lists.
list_one = myList[0]
list_two = myList[1]
Please let me know what you would recommend, thank you!
A custom generator can return two adjacent items at a time from a sequence without knowing the length:
def two(sequence):
i = iter(sequence)
a = next(i)
for b in i:
yield a,b
a = b
original = [[70,83,90],[19,25,30]]
result = [[abs(a-b) for a,b in two(sequence)]
for sequence in original]
print(result)
[[13, 7], [6, 5]]
Well, for each list, you can simply get its number of elements like this:
res = []
for my_list in list_of_lists:
res.append([])
for i in range(len(my_list) - 1):
# Do some stuff
You can then add the results you want to res[-1].

Replace multiple list elements at the same time (Python)

Given two lists (a and b), I'd like to replace three elements of list 'a' with three elements of list 'b'. Currently I am using an expression like this:
a[0], a[5], a[7] = b[11], b[99], b[2]
As I need to do such operations very frequently with lots of different arrays I am wondering if there is a more compact solution for this problem (the number of elements I need to replace is always 3 though). I was thinking about something like:
a[0,5,7] = b[11,99,2]
Which obviously does not work.
If you've a python list you can do something like this :
toReplace = [0,5,7]
targetIndices = [11, 99, 2]
for i,j in zip(toReplace, targetIndices): a[i] = b[j]
If you've a numpy array, it's even simpler :
a[toReplace] = b[targetIndices]
#i.e, a[[0,5,7]] = b[[11, 99, 2]]
There might be some better solutions but this does the trick:
ind1 = [0,5,7]
ind2 = [11,99,2]
for i in range(len(ind1)):
a[ind1[i]]=b[ind2[i]]

np.where in pandas, checking for empty lists

I have a DataFrame like this:
df = pd.DataFrame({'var1':['a','b','c'],
'var2':[[],[1,2,3],[2,3,4]]})
I would like to create a third column which gives the value in var1 if the corresponding list in var2 is empty, and the first element of the list in var2 otherwise. So my intended result is:
target = pd.DataFrame({'var1':['a','b','c'],
'var2':[[],[1,2,3],[2,3,4]],
'var3':['a',1,2]})
I've tried using np.where like this:
df['var3'] = np.where(len(df['var2'])>0 , df['var2'][0], df['var1'])
But it seems to be checking the length of the whole column rather than the length of the list within each row of the column. How can I get it to apply the condition to each row?
I have the same problem when I use bool(df['var2']) as my condition.
Let's use .str accessors and len:
df['var'] = np.where(df.var2.str.len() > 0, df.var2.str[0], df.var1)
Output:
var1 var2 var
0 a [] a
1 b [1, 2, 3] 1
2 c [2, 3, 4] 2
You could use a list comprehension:
v3 = [row['var1'] if len(row['var2'])==0 else row['var2'][0]
for i, row in df.iterrows()]
df['var3']=v3
Alternatively, you could use apply instead of where, to apply it to the whole dataframe:
First you need a function to use in apply
def f(row):
if len(row['var2'])==0:
return row['var1']
else:
return row['var2'][0]
Then apply it:
df['var3']= df.apply(f,axis=1)
It sounds like a post digging, but i would prefer use np.where because of vectorization than list comprehension (too time costy) or apply. A lot of online tutorial deeply explain the mechanism like here.

Groovy: Add some element of list to another list

list1 = [[1,'Rob','Ben', 'Ni', 'cool'],[2,'Jack','Jo','Raj','Giri'],[...]....]
list2 = [['20 May 2013',20],['25 May 2013',26],[...]....]
there will be 100 of such records
i want the resulting list like
list1 = [[1, '20 May 2013', 20, 'Rob','Ben','Ni', 'cool'],[2,'25 May 2013', 26, 'Jack','Jo','Raj','Giri']]
any suggestion ?
[list1, list2].transpose()*.flatten()
Assuming cardinality of list1 and list2 is same.
UPDATE
Question is modified drastically now, but you can get what you seek by extending the transpose as below:
[list1, list2].transpose()*.flatten()
.collect{[it[0], it[-2..-1], it[1..-3]]}*.flatten()
How about this?
def i = 0
def combined = list1.collect([]) { it + list2[i++] }
The result row isn't the concatenation of the two list rows - it takes the second row and inserts it into index 1 of the first row, without the last two elements. So
[list1, list2].transpose()*.flatten()
won't work - tho' it is pretty cool :-). However,
[list1,list2].transpose().collect { def l = it.flatten(); l[0..0] + l[5..6] + l[1..2] }
gives the result show in the question. And I fully admit to standing on the backs of giants :-)

Resources