Lesen Sie die Datei mit den wiederholten Paaren "Schlüssel = Wert" in DataFrame

11

Ich habe eine TXT-Datei mit Daten in diesem Format. Die ersten 3 Zeilen wiederholen sich immer wieder.

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

Ich möchte die Daten in einem Tabellenformat ausgeben, zum Beispiel:

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

Ich habe Mühe, die Header zu setzen und nur die Daten zu durchlaufen. Was ich bisher versucht habe ist:

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)

Die Ausgabe davon ist

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

Nicht wirklich das, wonach ich suche.

Flenters
quelle

Antworten:

2

Bei dieser Lösung wird davon ausgegangen, dass das Textformat dem von Ihnen beschriebenen entspricht. Sie können es jedoch so ändern, dass ein anderes Wort verwendet wird, um den Anfang einer neuen Zeile zu kennzeichnen. Hier nehmen wir an, dass eine neue Zeile mit dem nameFeld beginnt . Ich habe Ihre myfile()Funktion unten geändert , hoffe, es gibt Ihnen einige Ideen :)

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
quelle
10

Sie können Pandas verwenden, um die Datei zu lesen und die Daten zu verarbeiten. Sie können dies verwenden:

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 Ausgänge:

0     class grade name
index                 
0         B     A    1
1         A     D    2
luigigi
quelle
Fügen Sie hinzu df = pd.read_table(file, header=None), machen Sie die folgende Zeile new = df[0].str.split("=", n=1, expand=True), und dies wäre meine Lieblingsantwort in Bezug auf "netten Code".
MrFuppes
@ MrFuppes Ich habe meine Antwort bearbeitet. Danke für den Tipp.
Luigigi
1
+1 ;-) Ich lief jedoch nur %timeitgegen meine Antwort und war überrascht, wie langsam die reine Pandas-Lösung ist. Auf meinem Computer war es ungefähr x7 langsamer (für eine sehr kleine txt-Eingabedatei)! Mit der Bequemlichkeit kommt Overhead, mit Overhead (meistens) kommt Leistungsverlust ...
MrFuppes
7

Ich weiß, dass Sie genug Antworten haben, aber hier ist eine andere Möglichkeit, dies mit dem Wörterbuch zu tun:

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)

Dies gibt Ihnen die Ausgabe als:

name grade class
0    1     A     B
1    2     D     A

Nur um eine andere Perspektive zu bekommen.

SSharma
quelle
3

Da Sie eine Ausgabe haben, würde ich mit dem Problem folgendermaßen umgehen:

Erstellen Sie zunächst einen eindeutigen Index basierend auf der Wiederholbarkeit der Spalten.

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

Wir verwenden dies dann, um Ihren Datenrahmen mithilfe der crosstabFunktion zu schwenken

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
quelle
3

Sie können Ihre Textdatei auch filein Dreierblöcken lesen , eine verschachtelte Liste erstellen und diese in einen Datenrahmen einfügen:

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 wäre direkt

  name grade class
0    1     A     B
1    2     D     A

Hinweis Nr. 1: Obwohl dies mehr Codezeilen als eine reine pandasLösung ergibt, ist es meiner Erfahrung nach wahrscheinlich effizienter, da weniger pandasFunktionen und damit weniger Overhead verwendet werden.

Anmerkung 2: Im Allgemeinen würde ich argumentieren, dass es besser wäre, Ihre Eingabedaten in einem anderen Format zu speichern, z . B. jsonoder csv. das würde das Lesen viel einfacher machen, zum Beispiel mit der pandasFunktion read_csv im Fall einer CSV-Datei.

MrFuppes
quelle
0

Sie können diese Ausgabe mithilfe des Python-Wörterbuchmoduls und von Pandas generieren .

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)

Dieser Ansatz ist möglicherweise nicht der effizienteste, verwendet jedoch keine der erweiterten Funktionen von Pandas. Ich hoffe es hilft.

Die Ausgabe:

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

IMHO sehen alle aktuellen Antworten zu kompliziert aus. Was ich tun würde, ist, '='als sepParameter pd.read_csvzum Lesen von 2 Spalten und dann pivotden erhaltenen DataFrame zu verwenden:

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

Wenn Sie diesen mehrstufigen Spaltenindex nicht im Ergebnis haben möchten, können Sie ihn entfernen durch:

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