How can I change the location of columns without using pandas

Viewed 113

I have a matrix as shown below;

matrix="""  8 1 A A A A 8
            8 5 A A A 3 8
            7 2 A A 1 4 7
            6 1 3 A 2 5 7
            2 4 5 A 1 1 1"""

And here's my code block:

lines= [i.strip().split() for i in matrix.splitlines()]
lst=[[lines[i][j] for i in range(len(lines))]for j in range(len(lines[0]))]
h=0
while h<=len(lines[0]):
    for i in range(len(lines[0])-1,0,-1):
        for j in range(len(lines)-1,-1,-1):
            for k in lst:
                if k.count('A')==len(lines):
                    if lines[j][i-1]=='A':
                        lines[j][i-1]=lines[j][i]
                        lines[j][i]='A'
    h+=1
for line in lines:
    print(*(i or " " for i in line) , sep=" ")

If a column full of A's, I want to move that column to the most right side, but my code moves all A's to right. This is my output:

8 1 8 A A A A
8 5 3 8 A A A
7 2 1 4 7 A A
6 1 3 2 5 7 A
2 4 5 1 1 1 A

I want a output something like that:

8 1 A A A 8 A
8 5 A A 3 8 A
7 2 A 1 4 7 A
6 1 3 2 5 7 A
2 4 5 1 1 1 A 
3 Answers

You can use zip() to transpose the matrix, sort all that are pure "A" to its end and reverse-transpose with zip() again:

matrix="""  8 1 A A A A 8
            8 5 A A A 3 8
            7 2 A A 1 4 7
            6 1 3 A 2 5 7
            2 4 5 A 1 1 1"""

# string to list of lists of strings
m = [[x.strip() for x in line.split()] for line in matrix.split("\n")]
print(*m,sep="\n")

# transpose and sort
t_m = [list(line) for line in zip(*m)]
t_m.sort(key = lambda x: all(k=="A" for k in x))

# reverse transpose
m = [list(line) for line in zip(*t_m)]
print(*m,sep="\n")

Output:

# before
['8', '1', 'A', 'A', 'A', 'A', '8']
['8', '5', 'A', 'A', 'A', '3', '8']
['7', '2', 'A', 'A', '1', '4', '7']
['6', '1', '3', 'A', '2', '5', '7']
['2', '4', '5', 'A', '1', '1', '1']

# after
['8', '1', 'A', 'A', 'A', '8', 'A']
['8', '5', 'A', 'A', '3', '8', 'A']
['7', '2', 'A', '1', '4', '7', 'A']
['6', '1', '3', '2', '5', '7', 'A']
['2', '4', '5', '1', '1', '1', 'A']

The transposed data looks like this:

# before sorting
['8', '8', '7', '6', '2']
['1', '5', '2', '1', '4']
['A', 'A', 'A', '3', '5']
['A', 'A', 'A', 'A', 'A']  # this is the column you want to sort behind all others
['A', 'A', '1', '2', '1']
['A', '3', '4', '5', '1']
['8', '8', '7', '7', '1']

# after sort
['8', '8', '7', '6', '2']
['1', '5', '2', '1', '4']
['A', 'A', 'A', '3', '5']
['A', 'A', '1', '2', '1']
['A', '3', '4', '5', '1']
['8', '8', '7', '7', '1']
['A', 'A', 'A', 'A', 'A']  # now it is here

The sort/sorting works because it is only True if the whole row consists of 'A' (True == 1) all others are False == 0.

Sorting is stable so it does not change the relative orders between rows that evaluate False.

First of all, let's define utility function for transposing list of lists like

def transpose(elements):
    return list(map(list, zip(*elements)))

then let's define sorting key by the rule: if list elements are equal to 'A', then it goes last, else keep it in its original position

def sorting_key(indexed_row):
    return all(element == 'A' for element in indexed_row[1]), indexed_row

After that our solution will be

>>> from operator import itemgetter
>>> transpose(map(itemgetter(1), sorted(enumerate(transpose(lines)),
                                        key=sorting_key)))

which gives us

[['8', '1', 'A', 'A', 'A', '8', 'A'],
 ['8', '5', 'A', 'A', '3', '8', 'A'],
 ['7', '2', 'A', '1', '4', '7', 'A'],
 ['6', '1', '3', '2', '5', '7', 'A'],
 ['2', '4', '5', '1', '1', '1', 'A']]

or as @PatrickArtner pointed out, sorted uses Timsort, which is stable algorithm, so we don't need to handle case with leaving non-all-"A"-columns in their original places and define:

def sorting_key(row):
    return all(element == 'A' for element in row)

after that we can simply write

>>> transpose(sorted(transpose(lines),
                     key=sorting_key))

Here's a way using numpy:

s = np.array([x.strip() for x in matrix.split()]).reshape(5,7)
print(s)

array[['8', '1', 'A', 'A', 'A', 'A', '8'],
     ['8', '5', 'A', 'A', 'A', '3', '8'],
     ['7', '2', 'A', 'A', '1', '4', '7'],
     ['6', '1', '3', 'A', '2', '5', '7'],
     ['2', '4', '5', 'A', '1', '1', '1']]

You could use np.flatnonzero to locate columns where all values are 0, and swap them with the last:

m = np.flatnonzero((s == 'A').all(axis=0))[0]
s.T[[m, s.shape[1]-1]] = s.T[[s.shape[1]-1, m]]

array([['8', '1', 'A', '8', 'A', 'A', 'A'],
       ['8', '5', 'A', '8', 'A', '3', 'A'],
       ['7', '2', 'A', '7', '1', '4', 'A'],
       ['6', '1', '3', '7', '2', '5', 'A'],
       ['2', '4', '5', '1', '1', '1', 'A']], dtype='<U21')
Related