Преобразование неструктурированных блоков данных по столбцам (DataFrame) - PullRequest
0 голосов
/ 02 октября 2018

Описание проблемы:

У меня есть внешний файл * .xls, который я преобразовал в файл * .csv, содержащий блок данных, такой как:

"Legend number one";;;;Number of items;6
X;-358.6806792;-358.6716338;;;
Y;0.8767189;0.8966855;Avg;;50.1206378
Z;-0.7694626;-0.7520983;Std;;-0.0010354
D;8.0153902;8;Err;;1.010385
;;;;;

Естьмного много блоков.

Каждый блок может содержать некоторые дополнительные строки данных;

"Legend number six";;;;Number of items;19
X;-358.6806792;-358.6716338;;;
Y;0.8767189;0.8966855;Avg;;50.1206378
Z;-0.7654644;-0.75283;Std;;-0.0010354
D;8.0153902;8;Err;;1.010385
A;0;1;Value;;0
B;1;0;;;
;;;;;

Структура такова, что новая пустая строка отделяет каждый блок, то есть ';;;;;;»линия в моих образцах.

Первая строка после этого начинается с уникального идентификатора блока.

Кажется, что каждая строка содержит 6 элементов, таких как key1;elem1;elem2;key2;elem3;elem4, которые было бы неплохо представить в виде двух 3-элементоввектор key1;elem1;elem2 и key2;elem3;elem4 на двух отдельных строках.Пример для второго примера:

"Legend number six";;
;;Number of items;19
X;-358.6806792;-358.6716338;
;;
Y;0.8767189;0.8966855;
Avg;;50.1206378
Z;-0.7654644;-0.75283;
Std;;-0.0010354
D;8.0153902;8;
Err;;1.010385
A;0;1;
Value;;0
B;1;0;
;;
;;;;;

Некоторые пустые, но я не хочу их сейчас отбрасывать.Но я хотел бы закончить DataFrame, содержащий элементы по столбцам для каждого блока данных.

Самое чистое «предварительное решение», которое у меня есть:

С этим кодом Python я оказался вболее организованный «Список словарей»:

import os, sys, re, glob
import pandas as pd
csvFile = os.path.join(workingDir,'file.csv')
h = 0 # Number of lines to skip in head
s = 2 # number of values per key
s += 1
str1 = 'Number of items' 

# Reading file in a global list and storing each line in a sublist:
A = [line.split(';') for line in open(csvFile).read().split('\n')]
# This code splits each 6-elements sublist in one new sublist 
# containing two-elements; each element with 3 values:
B = [(';'.join(el[:s])+'\n'+';'.join(el[s:])).split('\n') for el in A] 

# Init empty structures:
names = [] # to store block unique identifier (the name in the legend)
L = [] # future list of dictionnaries

for el in (B):
    for idx,elj in enumerate(el):
        vi = elj.split(';')[1:]
        # Here we grep the name only when the 2nd element of 
        # the first line contains the string "Number of items", 
        # which is constant all over the file:
        if len(vi)>1 and vi[0]==str1:
            name = el[idx-1].split(';')[0]
            names.append(name)
            #print(name)

# We loop again over B to append in a new list one dictionary 
# per vector of 3 elements because each vector of 3 elements 
 # is structured like ; key;elem1;elem2          
for el in (B):
    for elj in (el):
        k = elj.split(';')[0]
        v = elj.split(';')[1:]
        # Little tweak because the key2;elem3;elem4 of the 
        # first line (the one containing the name) have the 
        # key in the second place like "elem3;key2;elem4" :
        if len(v)>1 and v[0]==str1:            
            kp = v[0]
            v = [v[1],k]
            k = kp
        if k!='':
            dct = {k:v}
            L.append(dct)

Мне не удалось извлечь имя в качестве глобального идентификатора и все значения блоков до сих пор как переменные.Я не могу играть с какой-то методикой, основанной на модуле, из-за переменного количества информации в каждом отдельном блоке данных, даже если все блоки содержат хотя бы несколько общих ключей.
Я также пробовал условие while в пределах forцикл по всему словарю, но теперь это беспорядок.
zip может быть хорошим вариантом, но я не знаю, как правильно его использовать.

Target DataFrame:

То, что я хотел бы получить, в идеале должно выглядеть примерно как DataFrame, содержащий:

index                'Number of items'    'X'    ''  'Y'  'Avg'  'Z'   'Std' ...
"Legend number one"    6                  ...
"Legend number six"   19                  ...
"Legend number 11"     6                  ...
"Legend number 15"    18                  ...

Имена столбцов - это ключи, а таблица содержит значения для каждого блока данных в отдельной строке..
Если есть нумерованный индекс и новый столбец с именем легенды;все в порядке.

Пример CSV для игры:

"Legend number one";;;;Number of items;6
X;8.6806792;8.6716338;;;
Y;0.1557;0.1556;Avg;;50.1206378
Z;-0.7859;-0.7860;Std;;-0.0010354
D;8.0153902;8;Err;;1.010385
;;;;;
"Legend number six";;;;Number of items;19
X;56.6806792;56.6716338;;;
Y;0.1324;0.1322;Avg;;50.1206378
Z;-0.7654644;-0.75283;Std;;-0.0010354
D;8.0153902;8;Err;;1.010385
A;0;1;Value;;0
B;1;0;;;
;;;;;
"Legend number 11";;;;Number of items;6
X;358.6806792;358.6716338;;;
Y;0.1324;0.1322;Avg;;50.1206378
Z;-0.7777;-0.7778;Std;;-0.0010354
D;8.0153902;8;Err;;1.010385
;;;;;
"Legend number 15";;;;Number of items;18
X;58.6806792;58.6716338;;;
Y;0.1324;0.1322;Avg;;50.1206378
Z;0.5555;0.5554;Std;;-0.0010354
D;8.0153902;8;Err;;1.010385
A;0;1;Value;;0
B;1;0;;;
C;0;0;k;1;0
;;;;;

Я использую Ubuntu и Python 3.6, но сценарий должен работать и на компьютере с Windows.

1 Ответ

0 голосов
/ 02 октября 2018

Добавление этого к предыдущему коду должно работать очень хорошо:

for elem in L:
    for key,val in elem.items():
        if key in names:
            name = key
            Dict2 = {}
        else:
            Dict2[key] = val
        Dict1[name] = Dict2

df1 = pd.DataFrame.from_dict(Dict1, orient='index')
df2 = pd.DataFrame(index=df1.index)
for col in df1.columns:
    colS = df1[col].apply(pd.Series)
    colS = colS.rename(columns = lambda x : col+'_'+ str(x))
    df2 = pd.concat([df2[:], colS[:]], axis=1)

df2.to_csv('output.csv', sep=',', index=True, header=True)

Возможно, есть много других способов ...

Эта ссылка была полезна:
https://chrisalbon.com/python/data_wrangling/pandas_expand_cells_containing_lists/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...