Baca file pasangan "key = value" yang diulang menjadi DataFrame

11

Saya punya file txt dengan data dalam format ini. 3 baris pertama ulangi berulang kali.

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

Saya ingin menampilkan data dalam format tabel, misalnya:

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

Saya berjuang untuk mengatur header dan hanya mengulang data. Apa yang saya coba sejauh ini adalah:

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)

Output dari itu adalah

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

Tidak benar-benar apa yang saya cari.

Flenters
sumber

Jawaban:

2

Solusi ini mengasumsikan format teks seperti yang telah Anda gambarkan, tetapi Anda dapat memodifikasinya untuk menggunakan kata lain untuk menunjukkan awal baris baru. Di sini, kami mengasumsikan bahwa baris baru dimulai dengan namebidang. Saya telah memodifikasi myfile()fungsi Anda di bawah ini, semoga memberi Anda beberapa ide :)

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.
kingfischer
sumber
10

Anda dapat menggunakan panda untuk membaca file dan memproses data. Anda dapat menggunakan ini:

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 Output:

0     class grade name
index                 
0         B     A    1
1         A     D    2
luigigi
sumber
tambahkan df = pd.read_table(file, header=None), buat baris berikut new = df[0].str.split("=", n=1, expand=True), dan ini akan menjadi jawaban favorit saya dalam hal "kode yang bagus".
MrFuppes
@ McFuppes Saya mengedit jawaban saya. Terima kasih atas petunjuknya.
luigigi
1
+1 ;-) Namun, saya hanya menjalankan %timeitjawaban saya dan terkejut betapa lambatnya solusi pure-panda. Itu sekitar x7 lebih lambat pada mesin saya (untuk file txt input sangat kecil)! Dengan kenyamanan datang di atas kepala, dengan overhead (sebagian besar waktu) datang hilangnya kinerja ...
MrFuppes
7

Saya tahu Anda memiliki jawaban yang cukup, tetapi berikut cara lain menggunakan kamus:

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)

Ini memberi Anda output sebagai:

name grade class
0    1     A     B
1    2     D     A

Hanya untuk mendapatkan perspektif lain.

SSharma
sumber
3

Karena Anda sudah mendapatkan hasil, inilah cara saya menangani masalahnya:

Pertama buat indeks unik berdasarkan pengulangan kolom,

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

kami kemudian menggunakan ini untuk memutar bingkai data Anda menggunakan crosstabfungsi

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
Datanovice
sumber
3

Yang juga bisa Anda lakukan adalah membaca file teks Anda filedi blok 3, membuat daftar bersarang, dan memasukkannya ke dalam kerangka data:

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 akan langsung menjadi

  name grade class
0    1     A     B
1    2     D     A

Catatan # 1: Meskipun ini membuat lebih banyak baris kode daripada pandassolusi murni , menurut pengalaman saya, ini cenderung lebih efisien karena menggunakan lebih sedikit pandasfungsi sehingga lebih sedikit overhead.

Catatan # 2: Secara umum saya berpendapat bahwa akan lebih baik untuk menyimpan data input Anda dalam format lain, misalnya jsonatau csv. itu akan membuatnya jauh lebih mudah dibaca, misalnya dengan pandasfungsi read_csv dalam kasus file csv.

MrFuppes
sumber
0

Anda dapat menghasilkan output dengan menggunakan modul Kamus Python dan 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)

Pendekatan ini mungkin bukan yang paling efisien tetapi tidak menggunakan salah satu fungsi canggih Pandas. Semoga ini bisa membantu.

Hasil:

    name    grade   class
0      1        A       B
1      2        D       A
Yash Ghorpade
sumber
0

IMHO, semua jawaban saat ini terlihat terlalu rumit. Apa yang akan saya lakukan, adalah menggunakan '='sebagai sepparameter pd.read_csvuntuk membaca 2 kolom, dan kemudian pivotDataFrame yang diperoleh:

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

Jika Anda tidak ingin indeks kolom multi-level dalam hasil, Anda dapat menghapusnya dengan:

df.columns = df.columns.get_level_values(1)
# 0 class grade name
# 0     B     A    1
# 1     A     D    2
Georgy
sumber