Make a list of two indexes in Pyomo (Python) - python

Good morning,
I've been searching through the internet a way to make a list or table with 2 indexes. Currently I've build a code for 1 index that is mass = list(range(0,value(m.n)+1)) and I fill this list with this expression mass[i] = value(m.mass[i]).
But now I have to introduce another variable that has two indexes as the following x[i,j] = value(m.x[i,j]). I've tried with x = []*value(m.ph). But what I want is to get something like x = [value(m.n)]*value(m.ph) in order to state that I have a table like. values (m.n) as rows and phases (m.ph) as columns.
The goal of doing so is graphing those variables with this function plt.plot(time,x). Idk if it will be possible to do this kind of list, table or matrix as you want to call it, but it will be very useful to know it because I got kinda stuck because of it.
My code is as following:
n = Param(initialize = 10, within = Integers)
N = Set(initialize = range(0,value(m.n)+1))
#What I got
x1 = [0,1,2,3,4,5,6,7,8,9,10]
x = list(range(0,value(m.n)+1))
for i in m.N:
x[i] = x1[i]
#What I try to do
ph = Param(initialize = 2, within = Integers)
Ph = Set(initialize = range(0,value(m.n)+1))
x2 = [[0,0],[1,1],[2,2],[3,3],[4,4]]
x = []*value(m.ph)
for i in m.N:
for j in m.Ph:
x[i,j] = x2[i,j]
The error is as following:
File "C:/Users/Manuel/Desktop/StackExample.py", line 38, in <module>
x[i,j] = x2[i,j]
TypeError: list indices must be integers or slices, not tuple
Thank you so much.
EDIT: What I want is create a list with two indexes in Python making those indexes have the length I required being these lenght defined by m.n = Param(initialize = 32, within = Integers) and m.ph = Param(initialize = 2, within = Integers).

Use dictionaries instead of lists. Dictionaries can be double indexed, which will make them indexed by tuples.
Writing your parameters should be:
paramName = Param(setOfIndiceA, setOfIndiceB, initialize=aDictionary)
Where setOfIndiceA and setOfIndiceB are the sets where fisrst (A) and second (B) indices of your dictionary are contained.
If (a,b) tuples from your indices are sparse (not all value of set A correspond to a value of set B and vice-versa), use one single set of tuples such as
listOfIndicesAB = [(1,1),(1,3),(2,1),(2,4)]
setOfIndicesAB = Set(initialize=listOfIndicesAB)

I found a way two convert the values from from a two dimensional pyomo variable to a two dimensional list. I am not sure if this is what you were looking for but what i understood from the headline. I assume that you indexed your variables with numbers
from pyomo.environ import *
model = ConcreteModel()
# Setup a two dimensional pyomo variable
model.set1 = RangeSet(0, 10)
model.set2 = RangeSet(0, 5)
# initialize with any number as example
model.variable = Var(model.set1, model.set2, initialize=3)
# do your optimization...
# Create an empty list with the same dimensions as the indexed variable
converted_list = [[None for j in range(len(model.set2))]
for i in range(len(model.set1))]
#call all touples and values from the variable class
#call the touple values from the dictionary and use them two index the list
for index in model.variable:
converted_list[index.__getitem__(0)][index.__getitem__(
1)] = model.variable[index].value
print(converted_list)
This gives you the initialized data or solution if you use your variable for an optimization.
[[3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3], [3, 3, 3, 3, 3, 3]]
You can call values like this from it:
print(converted_list[1][3])

Related

How do you sum together and merge multiple numbers in a list? (Python)

So, say I have the following list and variable i:
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 0, 0], [1, 2, 3, 2, 1] ]
i = 3
I would like to create a new list which will sum together and merge numbers from each sublist the ith element upwards to produce the following:
new_data = [ [1, 2, 5], [1, 2, 3], [1, 2, 6] ]
Where I have summed from each sublist the 3rd element upwards and merged the elements together. I would like to ask how, by using the standard library in Python, I can go about achieving this for an arbitrary (integer) value of i. I have the following idea in mind but I'm not sure how to put it into real Python code:
(pseudo-code)
new_data = []
for sublist in data:
new_data.append(elements before the ith element)
new_data.append(ith element onwards summed together)
You can slice your inner list by index and sum the rest with the one-liner:
>>> new_data = [row[:i-1] + [sum(row[i-1:])] for row in data]
>>> new_data
[[1, 2, 5], [1, 2, 5], [1, 2, 5]]
You can find a nice, "pythonic" one-liner in taras' answer:
new_data = [row[:i-1] + [sum(row[i-1:])] for row in data].
From pseudo-code to correct python code
I'll focus my answer on helping you transform your pseudo-code into python code.
Pseudo-code:
new_data = []
for sublist in data:
new_data.append(elements before the ith element)
new_data.append(ith element onwards summed together)
The first issue with your pseudo-code is that you are making a distinction between the list data and its sublists sublist, but you are not making a distinction between the list new_data and its sublists. Let me add a variable new_sublist in the loop:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.append(elements before the ith element)
new_sublist.append(ith element onwards summed together)
new_data.append(new_sublist)
The second issue with your pseudo code: you make two calls to .append in each iteration of the loop. However, these two calls are not similar: the first call is supposed to append several elements, whereas the second call is supposed to append one element. Python makes a distinction between the two operations; if you want to add more than one element at once, use .extend instead of .append. The code becomes:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.extend([elements before the ith element])
new_sublist.append(ith element onwards summed together)
new_data.append(new_sublist)
Finally we can turn your pseudo-code into proper python code, using a list slice to get the elements before the ith element, and builtin function sum along with a list slice to sum the ith element onwards:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.extend(sublist[:i])
new_sublist.append(sum(sublist[i:]))
new_data.append(new_sublist)
Note that in python, looping over a list to construct a new list is such a common operation that we use the compact and elegant list comprehension syntax to do it instead of a multiline for-loop:
new_data = [sublist[:i] + [sum(sublist[i:])] for sublist in data]
Relevant documentation
list.extend and list.append;
list slices;
builtin function sum;
list comprehensions.
You can do it like so:
def merger(data, pos):
pos -= 1 # 0 based, your i above is 1 based
# collection list for result
result = []
# go over each given inner part
for inner in data:
# add it up to the correct position
result.append(inner[:pos])
# extend it by the sum of the remainder values
result[-1].append(sum(inner[pos:]))
return result
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 1, 1], [1, 2, 3, 1, 1] ]
i = 3
print(merger(data,i))
Output:
[[1, 2, 5], [1, 2, 5], [1, 2, 5]]
or in short:
def merger(data, pos):
return [[inner[: pos - 1] + [sum(inner[pos - 1 :])] for inner in data]]
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 1, 1], [1, 2, 3, 1, 1] ]
i = 3
new_data = [sublist[:i-1] + [sum(sublist[i-1:])] for sublist in data]
print(new_data)
>>> [[1, 2, 5], [1, 2, 5], [1, 2, 5]]

Array Splitting in Python With Specific Input

If you are given two arrays as an input in the same line such as
[4,2,1,5,7],[4,1,2,3,5,7,1,2,7]
Is it possible to create separate arrays out of the above input?
arr1 = [4,2,1,5,7]
arr2 = [4,1,2,3,5,7,1,2,7]
I tried to use split(',') but since they are used in the actual arrays this does not work.
The length of the arrays can vary and the example above is just a sample.
Any help would be appreciated!
I would suggest "disguising" the input as a well-formed list by adding the outer brackets and then using literal_eval:
import ast
s = "[4,2,1,5,7],[4,1,2,3,5,7,1,2,7]"
parts = ast.literal_eval("[" + s + "]")
#[[4, 2, 1, 5, 7], [4, 1, 2, 3, 5, 7, 1, 2, 7]]
Or do not add anything and treat the input as a tuple of lists:
parts = ast.literal_eval(s)
#([4, 2, 1, 5, 7], [4, 1, 2, 3, 5, 7, 1, 2, 7])
This isn't the easy way, but if the goal is to learn to manipulate strings and lists, you can actually parse this the hard way as a stream of characters.
a = "[4,2,1,5,7],[45,1,2,3,5,7,100,2,7]"
l = []
current_n = ''
current_l = None
for c in a:
if c == '[':
current_l = []
elif c == ",":
if current_l is not None:
current_l.append(int(current_n))
current_n = ''
elif c.isdigit():
current_n += c
elif c == "]":
current_l.append(int(current_n))
l.append(current_l)
current_n = ''
current_l = None
l1, l2 = l
print(l1, l2)
# [4, 2, 1, 5, 7] [45, 1, 2, 3, 5, 7, 100, 2, 7]
Not something you would typically do, but a good exercise and it's simplicity should make is quite fast.
What you have there, once converted from a string using eval, is a 2-element tuple containing two lists. (The outer round parentheses are not mandatory in this situation.)
You could unpack it into two variables as follows:
str = '[4,2,1,5,7],[4,1,2,3,5,7,1,2,7]'
arr1, arr2 = eval(str)
Note: if the input string could derive from third-party input (for example in a server application) then eval should not be used for security reasons because it can allow for execution of arbitrary code, and ast.literal_eval should be used instead. (See separate answer by DYZ.) This will also return a 2-tuple of lists in the case of the input shown above, so the unpacking using var1, var2 = ... is unaffected.

Python: Inplace Merge sort implementation issue

I am implementing inplace merge sort algorithm in python3. Code takes an input array and calls it self recursively (with split array as input) if length of the input array is more than one. After that, it joins two sorted arrays. Here is the code
def merge_sort(array):
"""
Input : list of values
Note :
It divides input array in two halves, calls itself for the two halves and then merges the two sorted halves.
Returns : sorted list of values
"""
def join_sorted_arrays(array1, array2):
"""
Input : 2 sorted arrays.
Returns : New sorted array
"""
new_array = [] # this array will contain values from both input arrays.
j = 0 # Index to keep track where we have reached in second array
n2 = len(array2)
for i, element in enumerate(array1):
# We will compare current element in array1 to current element in array2, if element in array2 is smaller, append it
# to new array and look at next element in array2. Keep doing this until either array2 is exhausted or an element of
# array2 greater than current element of array1 is found.
while j < n2 and element > array2[j]:
new_array.append(array2[j])
j += 1
new_array.append(element)
# If there are any remaining values in array2, that are bigger than last element in array1, then append those to
# new array.
for i in range(j,n2):
new_array.append(array2[i])
return new_array
n = len(array)
if n == 1:
return array
else:
# print('array1 = {0}, array2 = {1}'.format(array[:int(n/2)], array[int(n/2):]))
array[:int(n/2)] = merge_sort(array[:int(n/2)])
array[int(n/2):] = merge_sort(array[int(n/2):])
# print('array before joining : ',array)
array = join_sorted_arrays(array[:int(n/2)],array[int(n/2):])
# print('array after joining : ',array)
return array
Now if the code is tested,
a = [2,1,4,3,1,2,3,4,2,7,8,10,3,4]
merge_sort(a)
print(a)
out : [1, 1, 2, 2, 3, 3, 4, 2, 3, 4, 4, 7, 8, 10]
If you uncomment the print statements in the above function, you will notice that, a = given output, just before the last call of join_sorted_arrays. After this function has been called, array 'a' should be sorted. To my surprise, if I do the following, output is correct.
a = [2,1,4,3,1,2,3,4,2,7,8,10,3,4]
a = merge_sort(a)
print(a)
out : [1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 7, 8, 10]
I need some help to understand why this is happening.
I am beginner, so any other comments about coding practices etc. are also welcome.
When you reassign array as the output of join_sorted_arrays() with
array = join_sorted_arrays(array[:int(n/2)],array[int(n/2):])
you're not updating the value of a anymore.
Seeing as you pass in a as the argument array, it's understandable why all variables named array in a function might seem like they should update the original value of array (aka a). But instead, what's happening with array = join_sorted_arrays(...) is that you have a new variable array scoped within the merge_sort() function. Returning array from the function returns that new, sorted, set of values.
The reference to a was being modified up until that last statement, which is why it looks different with print(a) after merge_sort(a). But you'll only get the final, sorted output from the returned value of merge_sort().
It might be clearer if you look at:
b = merge_sort(a)
print(a) # [1, 1, 2, 2, 3, 3, 4, 2, 3, 4, 4, 7, 8, 10]
print(b) # [1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 7, 8, 10]
Note that Python isn't a pass-by-reference language, and the details of what it actually is can be a little weird to suss out at first. I'm always going back to read on how it works when I get tripped up. There are plenty of SO posts on the topic, which may be of some use to you here.
For example, this one and this one.

Find four numbers in a list that add up to a target value

Below is the code I wrote in an attempt to solve this problem: Find four numbers in a list that add up to x.
def sum_of_four(mylist, x):
twoSum = {i+j:[i,j] for i in mylist for j in mylist}
four = [twoSum[i]+twoSum[x-i] for i in twoSum if x-i in twoSum]
print four
sum_of_four([2, 4, 1, 1, 4, 6, 3, 8], 8)
The answer I get for the sample input is:
[[1, 1, 3, 3], [1, 2, 3, 2], [3, 1, 3, 1], [3, 2, 1, 2], [3, 3, 1, 1]]
However, this list of lists contains duplicates. For example, [1,1,3,3] is the same as [3,3,1,1].
How can I print the list of lists without duplicate lists? I want to be as efficient as possible in runtime and space. Is it possible to change my list comprehension so that I don't print duplicates? I certainly do not want to sort the lists and then use set() to delete duplicates. I want to do something better.
A correct and relatively efficient approach starts by counting the number of times each value occurs in the input list. Suppose value occurs count times. Then you can append up to count copies of value to a list in which you build a selection of values. Before appending any copies of value, and after appending each copy, make a recursive call to move on to the next value.
We can implement this approach as follows:
length = 4
# Requires that frequencies be a list of (value, count) sorted by value.
def doit(frequencies, index, selection, sum, target, selections):
if index == len(frequencies):
return
doit(frequencies, index + 1, selection[:], sum, target, selections) # Skip this value.
value, count = frequencies[index]
for i in range(count):
selection.append(value)
sum += value
if sum > target:
return # Quit early because all remaining values can only be bigger.
if len(selection) == length:
if sum == target:
selections.append(selection)
return # Quit because the selection can only get longer.
doit(frequencies, index + 1, selection[:], sum, target, selections) # Use these values.
def sum_of_length(values, target):
frequency = {}
for value in values:
frequency[value] = frequency.setdefault(value, 0) + 1
frequencies = sorted(frequency.items()) # Sorting allows for a more efficient search.
print('frequencies:', frequencies)
selections = []
doit(frequencies, 0, [], 0, target, selections)
return list(reversed(selections))
print(sum_of_length([2, 4, 1, 1, 4, 6, 3, 8], 8))
print(sum_of_length([1, 1, 1, 2, 2, 3, 3, 4], 8))
print(sum_of_length([-1, -1, 0, 0, 1, 1, 2, 2, 3, 4], 3))
By the way, the correct answer for your sample input is [[1, 1, 2, 4]]. There is only one way to select four elements from [2, 4, 1, 1, 4, 6, 3, 8] such that their sum is 8.
If you want to reuse the numbers more than once such as your sample answer where you only have one 3 in the list but you have [1,1,3,3] as a solution, then you can try:
def sum_of_four(list, x, y, curr=[]):
if y == 1:
for l in list:
if l == x:
d = curr[:]
d.append(l)
print(d)
break
else:
for i in range(len(list)):
l = list[i]
if l <= (x - l) / (y - 1):
d = curr[:]
d.append(l)
sum_of_four(list[i:], x-l, y-1, d)
sum_of_four(sorted([2,4,1,4,6,3,8]),8,4)

Renumbering a 1D mesh in Python

First of all, I couldn't find the answer in other questions.
I have a numpy array of integer, this is called ELEM, the array has three columns that indicate, element number, node 1 and node 2. This is one dimensional mesh. What I need to do is to renumber the nodes, I have the old and new node numbering tables, so the algorithm should replace every value in the ELEM array according to this tables.
The code should look like this
old_num = np.array([2, 1, 3, 6, 5, 9, 8, 4, 7])
new_num = np.arange(1,10)
ELEM = np.array([ [1, 1, 3], [2, 3, 6], [3, 1, 3], [4, 5, 6]])
From now, for every element in the second and third column of the ELEM array I should replace every integer from the corresponding integer specified according to the new_num table.
If you're doing a lot of these, it makes sense to encode the renumbering in a dictionary for fast lookup.
lookup_table = dict( zip( old_num, new_num ) ) # create your translation dict
vect_lookup = np.vectorize( lookup_table.get ) # create a function to do the translation
ELEM[:, 1:] = vect_lookup( ELEM[:, 1:] ) # Reassign the elements you want to change
np.vectorize is just there to make things nicer syntactically. All it does is allow us to map over the values of the array with our lookup_table.get function
I actually couldn't exactly get what your problem is but, I tried to help you as far as I could understood...
I think you need to replace, for example 2 with 1, or 7 with 10, right? In such a case, you can create a dictionary for numbers that are to be replaced. The 'dict' below is for that purpose. It could also be done by using tuples or lists but for such purposes it is better to use dictionaries. Afterwards, just replace each element by looking into the dictionary.
The code below is a very basic one is relatively easy to understand. For sure there are more pythonic ways to do that. But if you are new into Python, the code below would be the most appropriate one.
import numpy as np
# Data you provided
old_num = np.array([2, 1, 3, 6, 5, 9, 8, 4, 7])
new_num = np.arange(1,10)
ELEM = np.array([ [1, 1, 3], [2, 3, 6], [3, 1, 3], [4, 5, 6]])
# Create a dict for the elements to be replaced
dict = {}
for i_num in range(len(old_num)):
num = old_num[i_num]
dict[num] = new_num[i_num]
# Replace the elements
for element in ELEM:
element[1] = dict[element[1]]
element[2] = dict[element[2]]
print ELEM

Categories