Đọc tệp của các cặp khóa lặp lại giá trị = giá trị các cặp đôi vào DataFrame


11

Tôi có một tệp txt với dữ liệu ở định dạng này. 3 dòng đầu tiên lặp đi lặp lại nhiều lần.

name=1
grade=A
class=B
name=2
grade=D
class=A

Tôi muốn xuất dữ liệu theo định dạng bảng, ví dụ:

name | grade | class
1    | A     | B
2    | D     | A

Tôi đang vật lộn để thiết lập các tiêu đề và chỉ lặp qua dữ liệu. Những gì tôi đã cố gắng cho đến nay là:

def myfile(filename):
    with open(file1) as f:
        for line in f:
            yield line.strip().split('=',1)

def pprint_df(dframe):
    print(tabulate(dframe, headers="keys", tablefmt="psql", showindex=False,))

#f = pd.DataFrame(myfile('file1')
df = pd.DataFrame(myfile('file1'))
pprint_df(df)

Đầu ra từ đó là

+-------+-----+
| 0     | 1   |
|-------+-----|
| name  | 1   |
| grade | A   |
| class | B   |
| name  | 2   |
| grade | D   |
| class | A   |
+-------+-----+

Không thực sự những gì tôi đang tìm kiếm.

Câu trả lời:


2

Giải pháp này giả định định dạng văn bản như bạn đã mô tả, nhưng bạn có thể sửa đổi nó để sử dụng một từ khác để biểu thị sự bắt đầu của một dòng mới. Ở đây, chúng tôi giả định rằng một dòng mới bắt đầu với nametrường. Tôi đã sửa đổi myfile()chức năng của bạn dưới đây, hy vọng nó cung cấp cho bạn một số ý tưởng :)

def myfile(filename):
    d_list = []
    with open(filename) as f:
        d_line = {}
        for line in f:
            split_line = line.rstrip("\n").split('=')  # Strip \n characters and split field and value.
            if (split_line[0] == 'name'):
                if d_line:
                    d_list.append(d_line)  # Append if there is previous line in d_line.
                d_line = {split_line[0]: split_line[1]}  # Start a new dictionary to collect the next lines.
            else:
                d_line[split_line[0]] = split_line[1]  # Add the other 2 fields to the dictionary.
        d_list.append(d_line) # Append the last line.
    return pd.DataFrame(d_list)  # Turn the list of dictionaries into a DataFrame.

10

Bạn có thể sử dụng gấu trúc để đọc tệp và xử lý dữ liệu. Bạn có thể sử dụng điều này:

import pandas as pd
df = pd.read_table(r'file.txt', header=None)
new = df[0].str.split("=", n=1, expand=True)
new['index'] = new.groupby(new[0])[0].cumcount()
new = new.pivot(index='index', columns=0, values=1)

new Đầu ra:

0     class grade name
index                 
0         B     A    1
1         A     D    2

thêm df = pd.read_table(file, header=None), tạo dòng sau new = df[0].str.split("=", n=1, expand=True)và đây sẽ là câu trả lời yêu thích của tôi về "mã đẹp".
MrFuppes

@MrFuppes Tôi đã chỉnh sửa câu trả lời của mình. Cảm ơn đã gợi ý.
luigigi

1
+1 ;-) tuy nhiên, tôi chỉ chạy %timeitngược lại câu trả lời của mình và đã ngạc nhiên khi thấy giải pháp gấu trúc thuần chủng chậm đến mức nào. Đó là khoảng x7 chậm hơn trên máy của tôi (đối với tệp txt đầu vào rất nhỏ)! Với sự tiện lợi đến từ trên cao, với chi phí hoạt động (hầu hết thời gian) sẽ mất hiệu suất ...
MrFuppes

7

Tôi biết bạn có đủ câu trả lời, nhưng đây là một cách khác để làm điều đó bằng từ điển:

import pandas as pd
from collections import defaultdict
d = defaultdict(list)

with open("text_file.txt") as f:
    for line in f:
        (key, val) = line.split('=')
        d[key].append(val.replace('\n', ''))

df = pd.DataFrame(d)
print(df)

Điều này cung cấp cho bạn đầu ra như:

name grade class
0    1     A     B
1    2     D     A

Chỉ để có được một góc nhìn khác.


3

Như bạn đã có một đầu ra, đây là cách tôi sẽ giải quyết vấn đề:

Đầu tiên tạo một chỉ mục duy nhất dựa trên độ lặp lại của các cột,

df['idx'] = df.groupby(df['0'])['0'].cumcount() + 1
print(df)
        0  1  idx
0   name  1      1
1  grade  A      1
2  class  B      1
3   name  2      2
4  grade  D      2
5  class  A      2

sau đó chúng tôi sử dụng điều này để xoay vòng khung dữ liệu của bạn bằng cách sử dụng crosstabhàm

df1 = pd.crosstab(df['idx'],df['0'],values=df['1'],aggfunc='first').reset_index(drop=True)
print(df1[['name','grade','class']])
0 name grade class
0    1     A     B
1    2     D     A

3

Những gì bạn cũng có thể làm là đọc tệp văn bản của mình filetrong các khối 3, xây dựng một danh sách lồng nhau và đặt nó vào một khung dữ liệu:

from itertools import zip_longest
import pandas as pd

# taken from https://docs.python.org/3.7/library/itertools.html:
def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

data = [['name', 'grade', 'class']]
with open(file, 'r') as fobj:
    blocks = grouper(fobj, 3)
    for b in blocks:
        data.append([i.split('=')[-1].strip() for i in b])

df = pd.DataFrame(data[1:], columns=data[0])  

df sẽ trực tiếp

  name grade class
0    1     A     B
1    2     D     A

Lưu ý # 1: Mặc dù điều này tạo ra nhiều dòng mã hơn là một pandasgiải pháp thuần túy , nhưng theo kinh nghiệm của tôi, nó có khả năng hiệu quả hơn vì nó sử dụng ít pandaschức năng hơn do đó ít chi phí hơn.

Lưu ý # 2: Nói chung tôi sẽ lập luận rằng sẽ tốt hơn nếu lưu trữ dữ liệu đầu vào của bạn ở định dạng khác, ví dụ jsonhoặc csv. điều đó sẽ làm cho nó dễ đọc hơn nhiều, ví dụ với pandashàm read_csv trong trường hợp tệp csv.


0

Bạn có thể tạo đầu ra đó bằng cách sử dụng mô-đun Từ điển và Pandas của Python .

import pandas as pd
from collections import defaultdict

text = '''name=1
          grade=A
          class=B
          name=2
          grade=D
          class=A'''
text = text.split()

new_dict = defaultdict(list) 
for i in text:
    temp = i.split('=')
    new_dict[temp[0]].append(temp[1])

df = pd.DataFrame(new_dict)

Cách tiếp cận này có thể không phải là cách hiệu quả nhất nhưng nó không sử dụng bất kỳ chức năng nâng cao nào của Pandas. Hy vọng nó giúp.

Đầu ra:

    name    grade   class
0      1        A       B
1      2        D       A

0

IMHO, tất cả các câu trả lời hiện tại trông quá phức tạp. Những gì tôi sẽ làm là sử dụng '='làm septham số pd.read_csvđể đọc 2 cột và sau đó pivotlà DataFrame thu được:

import pandas as pd

df = pd.read_csv('myfile', sep='=', header=None)
#        0  1
# 0   name  1
# 1  grade  A
# 2  class  B
# 3   name  2
# 4  grade  D
# 5  class  A

df = df.pivot(index=df.index // len(df[0].unique()), columns=0)
#       1           
# 0 class grade name
# 0     B     A    1
# 1     A     D    2

Nếu bạn không muốn chỉ mục cột đa cấp đó trong kết quả, bạn có thể xóa chỉ mục đó bằng cách:

df.columns = df.columns.get_level_values(1)
# 0 class grade name
# 0     B     A    1
# 1     A     D    2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.