2017-02-04 5 views
0

Предоставлено список GIT сообщений, где мерзавец коммит может следовать список измененных файлов, insertations и делеции, таких как следующие:панды: Чтение многострочного CSV, как вход с различными разделителями

import pandas as pd 
from io import StringIO 

data = '''\ 
f0a332fc65|User 1|2017-01-30 17:26:51|Merge branch 'dev' into master 
877134c7be|User 1|2017-01-30 14:46:55|commitmsg 1 
1 file changed, 15 insertions(+) 

557b90502d|User 1|2017-01-30 14:38:52|commitmsg 2 
10 files changed, 51 insertions(+), 56 deletions(-) 

052788be45|User 2|2017-01-30 14:29:28|commitmsg 3 
1 file changed, 1 deletion(-) 
''' 

df = pd.read_csv(StringIO(data), ????) 

Каждый за информацией об изменениях следует пустая строка, как показано в примере выше. Но не все коммиты разделяются пустой строкой (см. Первую строку, которая является фиксацией слияния, которая сама по себе не имеет информации об изменении).

Как будет выглядеть трансформация, которая дает следующий DataFrame?

  sha1 author     date       message \ 
0 f0a332fc65 User 1 2017-01-30 17:26:51 Merge branch 'dev' into master 
1 877134c7be User 1 2017-01-30 14:46:55      commitmsg 1 
2 557b90502d User 1 2017-01-30 14:38:52      commitmsg 2 
3 052788be45 User 2 2017-01-30 14:29:28      commitmsg 3 

    changes insertions deletions 
0  NaN   NaN  NaN 
1  1.0  15.0  NaN 
2  10.0  51.0  56.0 
3  1.0   NaN  1.0 

Этот вопрос может быть связан с несколькими входами линии, как обсуждалось here, но несколько сложнее.

У меня есть working solution, который читает файл на python и по существу извлекает информацию об изменении отдельно от остальной части и затем объединяет два DataFrames. Я думаю, что это можно сделать быстрее, без необходимости читать файл через python, но использовать только методы pandas io.

+1

Было бы хорошо, если бы вы смогли продемонстрировать решение, которое у вас есть, чтобы мы могли видеть, возможно ли сделать его более эффективным. И я считаю, что вы должны обрабатывать данные, прежде чем загружать их в DataFrame, так как это «непоследовательно» (варьируется количество строк перед каждой строкой). – hooy

+0

@hooy Спасибо за ваш ответ. Я загрузил свой код [здесь] (https://gist.github.com/dotcs/ea90eb3939ed8723c685a0ef6e045f73). Обратите внимание, что в моей собственной реализации коммиты слияния не отображаются, потому что я сосредоточился только на фиксациях, которые внесли изменения в базу кода. – dotcs

ответ

1

Рассмотрим спускаясь текст файл условная проверка для комбинаций изменение, Вставка и удаление, сохранение в список тем и добавление к более крупному списку, используемому в вызове pd.DataFrame().

rows = [] 
item = [] 

for line in StringIO(data): 
    if 'commitmsg' in line: 
     item = line.replace('\n', '').split('|') 

    elif 'changed' in line: 
     chg = [int(i[:3].strip()) for i in line.replace('\n', '').split(',')] 

     if 'insertion' in line and 'deletion' in line: 
      item.extend(chg)     

     elif 'insertion' in line:     
      item.extend(chg + [0])         

     elif 'deletion' in line:    
      item.extend([chg[0], 0, chg[1]]) 

     rows.append(item)     
     item = [] 

df = pd.DataFrame(rows, columns=['sha1', 'author', 'date', 'comment', 
           'changes', 'insertions', 'deletions'])  
print(df) 

#   sha1 author     date  comment changes insertions deletions 
# 0 877134c7be User 1 2017-01-30 14:46:55 commitmsg 1  1   15   0 
# 1 557b90502d User 1 2017-01-30 14:38:52 commitmsg 2  10   51   56 
# 2 052788be45 User 2 2017-01-30 14:29:28 commitmsg 3  1   0   1 
+1

Мне нравятся оба ваших подхода, ваш и один из @bunji. Я решил отметить ваш ответ как правильный, потому что он выполняет значительно быстрее. Я получил около 1 мс для этого решения и около 53 мс для другого. Спасибо вам обоим поделиться своими идеями. – dotcs

2

Вот один из способов, который позволяет читать все в панд в одном кадре, а затем требует последующей обработки, чтобы получить в результате dataframe в формате, который вы хотите:

import pandas as pd 
import numpy as np 

# read the data with comma OR pipe as the column separator 
df = pd.read_csv(StringIO(data), sep = ',|\|', header=None) 

# extract the number of changes (from column 0) and insert into column 4 
df[4] = df[0].str.extract('(\d+) files? changed') 

# extract the number of insertions (from column 1) and insert into column 5 
df[5] = df[1].str.extract('(\d+) insertions?') 

# extract the number of deletions (from column 1 or 2) and insert into column 6 
df[6] = df[1].str.extract('(\d+) deletions?').fillna('') + df[2].str.extract('(\d+) deletions?').fillna('') 

# replace empty strings with np.nan so they can be filled in later 
df[6] = df[6].replace('', np.nan) 

# make a mask of the rows you want to keep (in the end) 
keep_mask = df[0].str.match('^\w+$') 

# for the rows that contain change, insertion, deletion data only: 
# replace NaN values with 0 
df[~ keep_mask] = df[~ keep_mask].fillna(0, axis=1) 

# back fill any missing nan values (should only affect columns 4-6) 
# this should fill the row above each change, insertion, etc. row 
# with the appropriate values 
df.fillna(method = 'backfill', limit=1, inplace = True) 

# drop the rows that contain change, insertion, etc. data only 
df = df[keep_mask] 

# replace any 0 values with np.nan 
df.replace(0, np.nan, inplace=True) 

# name the columns what you want 
df.columns = ['sha1', 'author', 'date', 'message', 'changes', 'insertions', 'deletions'] 

print(df) 

     sha1 author     date       message \ 
0 f0a332fc65 User 1 2017-01-30 17:26:51 Merge branch 'dev' into master 
1 877134c7be User 1 2017-01-30 14:46:55      commitmsg 1 
3 557b90502d User 1 2017-01-30 14:38:52      commitmsg 2 
5 052788be45 User 2 2017-01-30 14:29:28      commitmsg 3 

    changes insertions deletions 
0  NaN  NaN  NaN 
1  1   15  NaN 
3  10   51  56 
5  1  NaN   1 
Смежные вопросы