อ่านไฟล์ของคู่“ key = value” ซ้ำ ๆ กันใน DataFrame


11

ฉันมีไฟล์ txt พร้อมข้อมูลในรูปแบบนี้ 3 บรรทัดแรกซ้ำไปซ้ำมา

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

ฉันต้องการส่งออกข้อมูลในรูปแบบตารางตัวอย่างเช่น:

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

ฉันกำลังพยายามตั้งค่าส่วนหัวและวนรอบข้อมูล สิ่งที่ฉันได้ลองไปแล้วคือ:

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)

ผลลัพธ์จากนั้นคือ

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

ไม่ใช่สิ่งที่ฉันกำลังมองหา

คำตอบ:


2

วิธีแก้ปัญหานี้ถือว่ารูปแบบข้อความเป็นไปตามที่คุณอธิบาย แต่คุณสามารถแก้ไขเพื่อใช้คำอื่นเพื่อแสดงจุดเริ่มต้นของบรรทัดใหม่ ที่นี่เราคิดว่าบรรทัดใหม่เริ่มต้นด้วยnameฟิลด์ ฉันได้ปรับเปลี่ยนการmyfile()ทำงานของคุณด้านล่างหวังว่ามันจะช่วยให้คุณมีความคิด :)

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

คุณสามารถใช้แพนด้าเพื่ออ่านไฟล์และประมวลผลข้อมูล คุณสามารถใช้สิ่งนี้:

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 ขาออก:

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

เพิ่มdf = pd.read_table(file, header=None)ทำบรรทัดต่อไปนี้new = df[0].str.split("=", n=1, expand=True)และนี่คือคำตอบที่ฉันโปรดปรานในแง่ของ "รหัสที่ดี"
MrFuppes

@MrFuppes ฉันแก้ไขคำตอบของฉัน ขอบคุณสำหรับคำใบ้
luigigi

1
+1 ;-) อย่างไรก็ตามฉันเพิ่ง%timeitจะตอบโต้กับคำตอบของฉันและรู้สึกว่าการแก้ปัญหาของแพนด้าบริสุทธิ์นั้นช้าเพียงใด มันช้าลงประมาณ x7 บนเครื่องของฉัน (สำหรับไฟล์ txt ที่เล็กมาก) ด้วยความสะดวกสบายมาเหนือศีรษะด้วยค่าใช้จ่าย (ส่วนใหญ่) สูญเสียประสิทธิภาพ ...
MrFuppes

7

ฉันรู้ว่าคุณมีคำตอบเพียงพอ แต่นี่เป็นอีกวิธีในการใช้พจนานุกรม:

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)

สิ่งนี้ให้ผลลัพธ์เป็น:

name grade class
0    1     A     B
1    2     D     A

เพียงเพื่อให้ได้มุมมองอื่น


3

เมื่อคุณได้รับผลลัพธ์นี่คือวิธีที่ฉันจะจัดการกับปัญหา:

ขั้นแรกให้สร้างดัชนีที่ไม่ซ้ำกันโดยพิจารณาจากความสามารถในการทำซ้ำของคอลัมน์

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

จากนั้นเราใช้สิ่งนี้เพื่อหมุนเดตาล็อกไฟล์ของคุณโดยใช้crosstabฟังก์ชั่น

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

สิ่งที่คุณสามารถทำได้คืออ่านไฟล์ข้อความของคุณfileในบล็อก 3 สร้างรายการซ้อนกันและใส่ไว้ใน dataframe:

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 จะเป็นโดยตรง

  name grade class
0    1     A     B
1    2     D     A

หมายเหตุ # 1:แม้ว่าสิ่งนี้จะทำให้เกิดรหัสบรรทัดมากกว่าpandasโซลูชันที่บริสุทธิ์แต่จากประสบการณ์ของฉันมีแนวโน้มที่จะมีประสิทธิภาพมากขึ้นเนื่องจากใช้pandasฟังก์ชั่นน้อยลงซึ่งส่งผลให้ค่าใช้จ่ายน้อยลง

หมายเหตุ # 2:โดยทั่วไปผมจะเถียงว่ามันจะดีกว่าที่จะเก็บข้อมูลป้อนข้อมูลของคุณในรูปแบบอื่นเช่นหรือjson csvที่จะทำให้ง่ายต่อการอ่านมากขึ้นเช่นpandasฟังก์ชั่นread_csvในกรณีของไฟล์ csv


0

คุณสามารถสร้างผลลัพธ์นั้นได้โดยใช้โมดูลพจนานุกรม Pythonและ Pandas

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)

วิธีนี้อาจไม่ใช่วิธีที่มีประสิทธิภาพที่สุด แต่ก็ไม่ได้ใช้ฟังก์ชั่นขั้นสูงของ Pandas หวังว่ามันจะช่วย

ผลลัพธ์:

    name    grade   class
0      1        A       B
1      2        D       A

0

IMHO คำตอบปัจจุบันทั้งหมดดูซับซ้อนเกินไป สิ่งที่ฉันจะทำคือใช้'='เป็นsepพารามิเตอร์ในpd.read_csvการอ่าน 2 คอลัมน์จากนั้นpivotDataFrame ที่ได้รับ:

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

หากคุณไม่ต้องการดัชนีคอลัมน์หลายระดับนั้นในผลลัพธ์คุณสามารถลบออกได้โดย:

df.columns = df.columns.get_level_values(1)
# 0 class grade name
# 0     B     A    1
# 1     A     D    2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.