Tekrarlanan “anahtar = değer” çiftlerinin dosyalarını DataFrame'e okuyun


11

Bu biçimdeki verileri içeren bir txt dosyası var. İlk 3 satır tekrar tekrar.

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

Örneğin, bir tablo biçiminde veri çıkışı istiyorum:

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

Üstbilgileri ayarlamak ve sadece veri üzerinde döngü için mücadele ediyorum. Şimdiye kadar denedim:

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)

Bundan çıktı

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

Gerçekten aradığım şey değil.

Yanıtlar:


2

Bu çözüm, metin biçiminin açıkladığınız gibi olduğunu varsayar, ancak yeni bir satırın başlangıcını belirtmek için farklı bir sözcük kullanacak şekilde değiştirebilirsiniz. Burada, namealanla yeni bir çizginin başladığını varsayıyoruz . myfile()Aşağıda işlevinizi değiştirdim , umarım size bazı fikirler verir :)

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

Dosyayı okumak ve verileri işlemek için pandaları kullanabilirsiniz. Bunu kullanabilirsiniz:

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 Çıktılar:

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

ekleyin df = pd.read_table(file, header=None), aşağıdaki satırı yapın new = df[0].str.split("=", n=1, expand=True)ve bu "güzel kod" açısından en sevdiğim cevap olacaktır.
MrFuppes

@MrFuppes Cevabımı düzenledim. İpucu için teşekkürler.
luigigi

1
+1 ;-) Ancak, ben sadece %timeitbenim cevap karşı bir koştu ve saf pandalar çözüm ne kadar yavaş şaşırdım. Makinemde yaklaşık x7 daha yavaştı (çok küçük bir giriş txt dosyası için)! Kolaylık ile yükü geliyor, yükü ile (çoğu zaman) performans kaybı geliyor ...
MrFuppes

7

Yeterli cevabınız olduğunu biliyorum, ancak burada sözlük kullanarak bunu yapmanın başka bir yolu var:

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)

Bu, çıktıyı şu şekilde verir:

name grade class
0    1     A     B
1    2     D     A

Sadece başka bir bakış açısı elde etmek için.


3

Bir çıktı var gibi ben bu sorunu ele nasıl olurdu:

İlk önce sütunların tekrarlanabilirliğini temel alan benzersiz bir dizin oluşturun,

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

daha sonra bu crosstabişlevi kullanarak veri çerçevenizi döndürmek için kullanırız

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

Yapabileceğiniz şey, metin dosyanızı file3 blok halinde okumak, iç içe bir liste oluşturmak ve bunu bir veri çerçevesine koymaktır:

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 doğrudan olurdu

  name grade class
0    1     A     B
1    2     D     A

Not # 1: Bu, saf bir pandasçözümden daha fazla kod satırı gerektirse de , tecrübelerime göre, daha az pandasişlev ve dolayısıyla daha az ek yük kullandığı için daha verimli olması muhtemeldir .

Not # 2: Genel olarak, giriş verilerinizi başka bir formatta, örneğin jsonveya daha iyi saklamanın daha iyi olacağını savunurum csv. bu da okumayı çok daha kolay hale getirir, örneğin bir csv dosyası olması durumunda read_csvpandas işlevi ile .


0

Bu çıktıyı Python'un Sözlük modülünü ve Pandaları kullanarak oluşturabilirsiniz .

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)

Bu yaklaşım en verimli yaklaşım olmayabilir, ancak Pandaların gelişmiş işlevlerinden hiçbirini kullanmaz. Umarım yardımcı olur.

Çıktı:

    name    grade   class
0      1        A       B
1      2        D       A

0

IMHO, tüm güncel cevaplar çok karmaşık görünüyor. Ne yapacağım, 2 sütun ve sonra elde edilen DataFrame okumak için '='bir sepparametre olarak kullanmaktır :pd.read_csvpivot

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

Sonuçta bu çok düzeyli sütun dizini istemiyorsanız, aşağıdaki yolla kaldırabilirsiniz:

df.columns = df.columns.get_level_values(1)
# 0 class grade name
# 0     B     A    1
# 1     A     D    2
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.