1

Suppose if we have a tabular CPD (conditional probability distribution) like this

| C   | C_0 | C_0 | C_0 | C_0 | C_1 | C_1 | C_1 | C_1 |
| B   | B_0 | B_0 | B_1 | B_1 | B_0 | B_0 | B_1 | B_1 |
| A   | A_0 | A_1 | A_0 | A_1 | A_0 | A_1 | A_0 | A_1 |
| J_0 | 0.9 | 0.3 | 0.9 | 0.3 | 0.8 | 0.8 | 0.4 | 0.4 |
| J_1 | 0.1 | 0.7 | 0.1 | 0.7 | 0.2 | 0.2 | 0.6 | 0.6 |

then there should be a method to switch the tuples inside the tables (for example if the user wants to switch B and A inside the table such that B is the most rapidly changing value).

Keep in mind the values also change accordingly. After switching A and B inside the table it should look something like this.

| C   | C_0 | C_0 | C_0 | C_0 | C_1 | C_1 | C_1 | C_1 |
| A   | A_0 | A_0 | A_1 | A_1 | A_0 | A_0 | A_1 | A_1 |
| B   | B_0 | B_1 | B_0 | B_1 | B_0 | B_1 | B_0 | B_1 |
| J_0 | 0.9 | 0.9 | 0.3 | 0.3 | 0.8 | 0.4 | 0.8 | 0.4 |
| J_1 | 0.1 | 0.1 | 0.7 | 0.7 | 0.2 | 0.6 | 0.2 | 0.6 |

So the problem boils down to:

Find new values according to new ordering of the headings where each heading can have a different cardinality(number of values it can take).

For this I have written the below method that gets the job done but that I don't find elegant or pythonic. This method is currently written for a single row which can be easily extended to the example given above of many lists.

def change_order(new_order, old_order, old_card, old_list):
    if (set(new_order) - set(old_order)) or (set(old_order) - set(new_order)):
            raise ValueError("New order either has missing or extra arguments")
    else:
            res = [-1]*len(old_list)
            for i in range(len(old_list)):
                    d = {}
                    idx = i
                    for card, var in zip(old_card, old_order):
                            #prod *= card
                            d[var] = idx%card,card
                            idx //= card
                    new_index = 0

                    prod = 1
                    for var in new_order:
                            new_index += d[var][0]*prod
                            prod *= d[var][1]
                    res[new_index] = old_list[i]


            return res



old_order = ['A','B','C']
old_card = [2,2,2]
new_order = ['B', 'A','C']
old_list = [0.9,0.3,0.9,0.3,0.8,0.8,0.4,0.4]

print(change_order(new_order, old_order, old_card, old_list))
[0.9, 0.9, 0.3, 0.3, 0.8, 0.4, 0.8, 0.4]

Idea behind what I have implemented can be taken from number (whole list is viewed as a number) representation in any base. So basically I am just rearranging the places in the new number. This is just to give an intuitive understanding of the code above. This explanation is very vague and should not be considered too seriously.

So, I want to ask if there is any other way to do this?

I was thinking of using reshape() method of numpy (list or numpy array both are acceptable to me, although any suggestions on which choice would be faster would be of great help). Even with reshape the best way I could come up with is one-to-one mapping between old ordering and new_ordering. So I googled and the best close answers I could find are this and this, but these don't sufficiently answer my question.

Edit:

I am making the requirements a little more stringent. One to one mapping is not acceptable. If there are some methods available in numpy or python that can reorder it neatly by slicing multiple columns at once to create new ordering it would be better.

P.S: I have found a way that I think meets the requirements, it is short, clear and uses already available methods to do the job and I have added it as answer below.

Community
  • 1
  • 1
Pukki
  • 586
  • 1
  • 7
  • 18
  • If this is **working code** that you think could be improved, see [codereview.se]. If not, please clarify the issue. – jonrsharpe Jan 14 '16 at 23:09
  • Yes, this is a working code but I want an entirely different approach. I mentioned this just to illustrate what I have tried. I don't really want comments on the approach that I have taken. – Pukki Jan 14 '16 at 23:10
  • Then this question is too broad for SO. – jonrsharpe Jan 14 '16 at 23:24
  • @jonrsharpe I am relatively new here but I don't think that this question is too broad. It is a programming question that asks specifically about how to rearrange elements according to above described constraints. It may be broad in the sense that it is open to many different possible answers, But the question in itself is specific. If there is any other forum where it would be better suited then please suggest. Thanks and pardon me for ignorance. – Pukki Jan 14 '16 at 23:29
  • *"It may be broad in the sense that it is open to many different possible answers"* - yes, exactly that sense. *"If there is any other forum where it would be better suited then please suggest"* - this isn't a clearinghouse for the rest of the internet. – jonrsharpe Jan 14 '16 at 23:30
  • If there's a built-in stable sort, you could sort the columns by B, then by A, then by C; this should give you the order in the second table. – m69's been on strike for years Jan 15 '16 at 00:37
  • @m69 Yes, stable sort in the order B, A and C should work just fine. I had considered it when I first faced the problem but for sorting to work I will need to represent the indices in binary where each place is associated with a letter. For eg.: CBA would be rearranged to CAB. So I guess I will need to keep another array for this purpose. Or is there some better approach? – Pukki Jan 15 '16 at 00:54
  • I'm not python-literate enough to offer further suggestions, unfortunately. – m69's been on strike for years Jan 15 '16 at 01:16

1 Answers1

1

I found a way to do this in another SO question, I am not deleting this as it may be a little hard to find the other question and it needs a few little tricks to work in my case. As it happens transpose method itself does it if we provide the new ordering. Below is a short implementation according to the link:

def change_order2(new_order, old_order, old_card, old_list):
    import numpy as np
    new_ord = [~old_order.index(letter) for letter in new_order]     
    old_list = np.array(old_list).reshape(old_card[::-1])
    new_list = np.transpose(old_list, new_ord[::-1]).flatten()
    return new_list
Community
  • 1
  • 1
Pukki
  • 586
  • 1
  • 7
  • 18