2012-01-16 28 views
10

Tôi có danh sách các danh sách đại diện cho một mạng lưới dữ liệu (nghĩ các hàng trong bảng tính). Mỗi hàng có thể có một số cột tùy ý và dữ liệu trong mỗi ô là một chuỗi có độ dài tùy ý.cách bình thường hóa danh sách các chuỗi trong python?

Tôi muốn bình thường hóa điều này, thực tế, làm cho mỗi hàng có cùng số cột và mỗi cột trong dữ liệu có cùng chiều rộng, đệm với khoảng trắng nếu cần. Ví dụ, với đầu vào sau:

(
("row a", "a1","a2","a3"), 
("another row", "b1"), 
("c", "x", "y", "a long string") 
) 

Tôi muốn các dữ liệu giống như thế này:

(
("row a  ", "a1", "a2", "a3   "), 
("another row", "b1", " ", "    "), 
("c   ", "x ", "y ", "a long string") 
) 

giải pháp pythonic cho python 2.6 hoặc cao hơn là gì? Chỉ cần được rõ ràng: Tôi không tìm cách để in đẹp danh sách mỗi se, tôi đang tìm một giải pháp trả về một danh sách mới của danh sách (hoặc bộ dữ liệu) với các giá trị đệm ra.

+1

Chỉ cần * hoàn toàn rõ ràng *: Bạn có muốn dữ liệu chứa trong bộ dữ liệu hoặc định dạng dưới dạng các chuỗi không? – Makoto

+0

Tôi muốn dữ liệu trong bộ dữ liệu, như đã nêu trong dòng cuối cùng của câu hỏi: "Tôi đang tìm một giải pháp trả về danh sách danh sách mới (hoặc bộ dữ liệu) với các giá trị được đệm". –

Trả lời

7

Bắt đầu với dữ liệu đầu vào của bạn:

>>> d = (
("row a", "a1","a2","a3"), 
("another row", "b1"), 
("c", "x", "y", "a long string") 
) 

Thực hiện một đường chuyền để xác định kích thước tối đa của mỗi cột:

>>> col_size = {} 
>>> for row in d: 
     for i, col in enumerate(row): 
      col_size[i] = max(col_size.get(i, 0), len(col)) 

>>> ncols = len(col_size) 

Sau đó thực hiện một đường chuyền thứ hai để pad mỗi cột để chiều rộng yêu cầu:

>>> result = [] 
>>> for row in d: 
     row = list(row) + [''] * (ncols - len(row)) 
     for i, col in enumerate(row): 
      row[i] = col.ljust(col_size[i]) 
     result.append(row) 

Điều đó mang lại kết quả mong muốn:

>>> from pprint import pprint 
>>> pprint(result) 
[['row a  ', 'a1', 'a2', 'a3   '], 
['another row', 'b1', ' ', '    '], 
['c   ', 'x ', 'y ', 'a long string']] 

Để thuận tiện, các bước có thể được kết hợp thành một hàm duy nhất:

def align(array): 
    col_size = {} 
    for row in array: 
     for i, col in enumerate(row): 
      col_size[i] = max(col_size.get(i, 0), len(col)) 
    ncols = len(col_size) 
    result = [] 
    for row in array: 
     row = list(row) + [''] * (ncols - len(row)) 
     for i, col in enumerate(row): 
      row[i] = col.ljust(col_size[i]) 
     result.append(row) 
    return result 
1
import itertools 

def fix_grid(grid): 
    # records the number of cols, and their respective widths 
    cols = [] 
    for row in grid: 
     # extend cols with widths of 0 if necessary 
     cols.extend(itertools.repeat(0, max(0, len(row) - len(cols))) 
     for index, value in enumerate(row): 
      # increase any widths in cols if this row has larger entries 
      cols[index] = max(cols[index], len(value) 
    # generate new rows with values widened, and fill in values that are missing 
    for row in grid:   
     yield tuple(value.ljust(width) 
        for value, width in itertools.zip_longest(row, cols, '')) 
# create a tuple of fixed rows from the old grid 
grid = tuple(fix_grid(grid)) 

Xem:

+1

Mã đó có nhiều lỗi trong đó. Bạn có thể chạy trên máy tính của mình không? Thiếu dấu ngoặc đơn đóng, tôi giả định 'zip_longest' có nghĩa là' izip_longest', có một vấn đề về thụt đầu dòng sau vòng lặp for và thậm chí khi sửa lỗi tôi vẫn gặp lỗi. –

+0

Đây là một mô hình dựa trên giải pháp của bạn và sử dụng Python 3. –

0

tôi chỉ có thể nghĩ để làm điều này bằng cách đi qua nó hai lần - nhưng không nên cứng:

def pad_2d_matrix(data): 
    widths = {} 
    for line in data: 
     for index, string in enumerate(line): 
      widths[index] = max(widths.get(index, 0), len(string)) 
    result = [] 
    max_strings = max(widths.keys()) 
    for line in data: 
     result.append([]) 
     for index, string in enumerate(line): 
      result[-1].append(string + " " * (widths[index] - len(string) )) 
     for index_2 in range(index, max_strings): 
      result[-1].append(" " * widths[index_2]) 
    return result 
1

tôi muốn đề nghị bạn sử dụng list thay vì tuple. tuple s là không thay đổi và khó làm việc.

Đầu tiên, hãy tìm độ dài của hàng dài nhất.

maxlen = max([len(row) for row in yourlist]) 

Sau đó pad mỗi hàng theo số cần thiết của chuỗi:

for row in yourlist: 
    row += ['' for i in range(maxlen - len(row))] 

Sau đó, bạn có thể trao đổi các hàng và cột ví dụ: cột nên hàng và ngược lại.Để làm điều đó, bạn có thể viết

newlist = [[row[i] for row in yourlist] for i in range(len(row))] 

Bây giờ, bạn có thể lấy một hàng (một cột danh sách cũ) và dán các chuỗi theo yêu cầu.

for row in newlist: 
    maxlen = max([len(s) for s in row]) 
    for i in range(len(row)): 
     row[i] += ' ' * (maxlen - len(row[i])) 

Bây giờ chuyển đổi bảng trở lại định dạng ban đầu:

table = [[row[i] for row in newlist] for i in range(len(row))] 

Để đặt nó lại với nhau trong một hàm:

def f(table): 
    maxlen = max([len(row) for row in table]) 
    for row in table: 
     row += ['' for i in range(maxlen - len(row))] 
    newtable = [[row[i] for row in table] for i in range(len(row))] 
    for row in newtable: 
     maxlen = max([len(s) for s in row]) 
     for i in range(len(row)): 
      row[i] += ' ' * (maxlen - len(row[i])) 
    return [[row[i] for row in newtable] for i in range(len(row))] 

Giải pháp này làm việc cho list s.

2

Trước hết, định nghĩa một hàm padding:

def padder(lst, pad_by): 
    lengths = [len(x) for x in lst] 
    max_len = max(lengths) 
    return (x + pad_by * (max_len - length) for x, length in zip(lst, lengths)) 

sau đó pad mỗi mục để cùng độ dài bằng '':

a = # your list of list of string 

a_padded = padder(a, ('',)) 

sau đó, transpose danh sách này trong danh sách vì vậy chúng tôi có thể làm việc cột theo cột,

a_tr = zip(*a_padded) 

cho mỗi hàng, chúng tôi tìm chiều dài tối đa của đường ings, và sau đó pad nó vào chiều dài quy định.

a_tr_strpadded = (padder(x, ' ') for x in a_tr) 

cuối cùng, chúng tôi chuyển tiếp lại và đánh giá kết quả.

a_strpadded = zip(*a_tr_strpadded) 
return [list(x) for x in a_strpadded] 

Nếu bạn muốn một bộ tuple thay vì danh sách danh sách.

Demo: http://ideone.com/4d0DE

+0

Đây là nhiều hơn hoặc ít hơn những gì tôi đang tìm kiếm, mặc dù điều này dường như yêu cầu python 3 (?). Ít nhất, mã demo không hoạt động trên bản cài đặt 2.7 của tôi: đối số 'TypeError: zip() sau * phải là một chuỗi, không phải máy phát điện' –

+0

@BryanOakley: Phải có một số vấn đề với cài đặt Python của bạn vì tôi đã đã thử, và nó hoạt động trên [Python 2.6.4 trên ideone] (http://ideone.com/GBeit) và Python 2.7.2 trên máy tính của tôi. – kennytm

6

Đây là những gì tôi đã đưa ra:

import itertools 

def pad_rows(strs): 
    for col in itertools.izip_longest(*strs, fillvalue=""): 
     longest = max(map(len, col)) 
     yield map(lambda x: x.ljust(longest), col) 

def pad_strings(strs): 
    return itertools.izip(*pad_rows(strs)) 

Và gọi nó như thế này:

print tuple(pad_strings(x)) 

mang lại kết quả này:

(('row a  ', 'a1', 'a2', 'a3   '), 
('another row', 'b1', ' ', '    '), 
('c   ', 'x ', 'y ', 'a long string')) 
-1

chỉ cho vui - o ne liner

from itertools import izip_longest as zl 


t=(
("row a", "a1","a2","a3"), 
("another row", "b1"), 
("c", "x", "y", "a long string") 
); 


b=tuple(tuple(("{: <"+str(map(max, (map(lambda x: len(x) if x else 0,i) for i in zl(*t)))[i])+"}").format(j) for i,j in enumerate(list(k)+[""]*(max(map(len,t))-len(k)))) for k in t) 
print(b) 
+1

đây là ví dụ tuyệt vời về thời điểm một lớp lót không ** thích hợp – jterrace

0

Tôi đồng ý với mọi người khác, rằng cần có hai thẻ. Pass 1 tính toán chiều rộng tối đa cho mỗi cột, và vượt qua 2 miếng mỗi tế bào với chiều rộng cột của nó.

Mã bên dưới dựa trên các hàm dựng sẵn của Python map()reduce(). Hạn chế là các biểu thức được cho là khó hiểu hơn. Tôi đã cố gắng bù đắp điều đó với rất nhiều thụt đầu dòng. Lợi ích là mã hưởng lợi từ bất kỳ sự tối ưu hóa vòng lặp nào mà việc triển khai đã thực hiện trong các hàm này.

g = (
("row a", "a1","a2","a3"), 
("another row", "b1"), 
(),  # null row added as a test case 
("c", "x", "y", "a long string") 
) 

widths = reduce(
     lambda sofar, row: 
      map(
       lambda longest, cell: 
        max(longest, 0 if cell is None else len(cell) 
       ), 
      sofar, 
      row 
     ), 
     g, 
     [] 
) #reduce() 

print 'widths:', widths 

print 'normalised:', tuple([ 
    tuple(map(
     lambda cell, width: ('' if cell is None else cell).ljust(width), 
     row, 
     widths 
    )) #tuple(map(
    for row in g 
]) #tuple([ 

Điều này cho phép đầu ra (với ngắt dòng gia tăng đối với mức độ dễ đọc):

widths: [11, 2, 2, 13] 
normalised: (
    ('row a  ', 'a1', 'a2', 'a3   '), 
    ('another row', 'b1', ' ', '    '), 
    ('   ', ' ', ' ', '    '), 
    ('c   ', 'x ', 'y ', 'a long string') 
) 

Tôi đã thử nghiệm mã này. Các biểu thức ... if cell is None else cell là tiết, nhưng cần thiết để làm cho các biểu thức thực sự hoạt động.

Các vấn đề liên quan