#python #pandas #csv #parsing #openfoam
#python #pandas #csv #синтаксический анализ #openfoam
Вопрос:
Я хочу прочитать и обработать файл CSV с помощью pandas. Файл (как показано ниже) содержит несколько строк заголовка, которые обозначены #
тегом. Я могу легко импортировать этот файл, используя
import pandas as pd
file = "data.csv"
data = pd.read_csv(file, delimiter="s ",
names=["Time", "Cd", "Cs", "Cl", "CmRoll", "CmPitch", "CmYaw", "Cd(f)",
"Cd(r)", "Cs(f)", "Cs(r)", "Cl(f)", "Cl(r)"],
skiprows=13)
Однако у меня много таких файлов с разными названиями заголовков, и я не хочу называть их ( Time Cd Cs ...
) вручную. Также количество прокомментированных строк отличается для каждого файла. Итак, я хочу автоматизировать эту задачу.
Должен ли я использовать здесь что-то вроде регулярного выражения перед передачей данных в pandas dataframe?
Спасибо за любой совет.
И да, имена заголовков также начинаются с #
.
data.csv:
# Force coefficients
# dragDir : (9.9735673312816520e-01 7.2660490528994301e-02 0.0000000000000000e 00)
# sideDir : (0.0000000000000000e 00 0.0000000000000000e 00 -1.0000000000000002e 00)
# liftDir : (-7.2660490528994315e-02 9.9735673312816520e-01 0.0000000000000000e 00)
# rollAxis : (9.9735673312816520e-01 7.2660490528994301e-02 0.0000000000000000e 00)
# pitchAxis : (0.0000000000000000e 00 0.0000000000000000e 00 -1.0000000000000002e 00)
# yawAxis : (-7.2660490528994315e-02 9.9735673312816520e-01 0.0000000000000000e 00)
# magUInf : 4.5000000000000000e 01
# lRef : 5.9399999999999997e-01
# Aref : 3.5639999999999999e-03
# CofR : (1.4999999999999999e-01 0.0000000000000000e 00 0.0000000000000000e 00)
#
# Time Cd Cs Cl CmRoll CmPitch CmYaw Cd(f) Cd(r) Cs(f) Cs(r) Cl(f) Cl(r)
5e-06 1.8990180226147195e 00 1.4919925634649792e-11 2.1950119509976829e 00 -1.1085971520784955e-02 -1.0863798447281650e 00 9.5910040927874810e-03 9.3842303978657482e-01 9.6059498282814471e-01 9.5910041002474442e-03 -9.5910040853275178e-03 1.1126130770676479e-02 2.1838858202270064e 00
1e-05 2.1428508927716594e 00 1.0045114197556737e-08 2.5051633252700962e 00 -1.2652317494411272e-02 -1.2367567798452046e 00 1.0822379290263353e-02 1.0587731288914184e 00 1.0840777638802410e 00 1.0822384312820453e-02 -1.0822374267706254e-02 1.5824882789843508e-02 2.4893384424802525e 00
...
Комментарии:
1. ваш заголовок всегда находится в последней позиции # lines?
2. На самом деле большинство файлов — так что да 🙂 РЕДАКТИРОВАТЬ: Да, это так.
Ответ №1:
Как насчет извлечения заголовка перед чтением файла? Мы только предполагаем, что ваши строки заголовка начинаются с #
. Извлечение заголовка, а также его положение в файле автоматизировано. Мы также гарантируем, что считывается не больше строк, чем необходимо (кроме первой строки данных).
with open(file) as f:
line = f.readline()
cnt = 0
while line.startswith('#'):
prev_line = line
line = f.readline()
cnt = 1
# print(prev_line)
header = prev_line.strip().lstrip('# ').split()
df = pd.read_csv(file, delimiter="s ",
names=header,
skiprows=cnt
)
С помощью этого вы также можете обрабатывать другие строки заголовка. Это также дает вам положение заголовка в файле.
Комментарии:
1. Это работает хорошо. Спасибо. Однако, поскольку мои файлы слишком большие, я буду читать только первые 50 строк, как указано @Manakin
2. @Sunsheep, мое решение считывает только строки заголовка первую строку данных. Итак, если ваш заголовок состоит из 13 строк, вы читаете 14 строк.
Ответ №2:
Это должно сработать, это просто и эффективно, это сводит переменные к минимуму и не требует никакого ввода, кроме имени файла.
with open(file, 'r') as f:
for line in f:
if line.startswith('#'):
header = line
else:
break #stop when there are no more #
header = header[1:].strip().split()
data = pd.read_csv(file, delimiter="s ", comment='#', names=header)
Сначала вы открываете файл и читаете только прокомментированную строку (это будет быстро и экономно с точки зрения памяти). Последняя допустимая строка будет окончательным заголовком, который будет очищен и преобразован в список. Наконец, вы открываете файл с помощью pandas.read_csv()
with comment='#'
, который пропустит прокомментированные строки, и names=header
.
Комментарии:
1. Работает хорошо. Спасибо. Похоже на ответ Стефана. Он был немного быстрее 🙂
Ответ №3:
Может помочь немного регулярных выражений. Это не самое красивое из решений, поэтому не стесняйтесь публиковать лучшее решение.
Давайте прочитаем первые 50 строк любого файла, чтобы найти последнее вхождение хэша, который должен быть именем столбца.
- ^ утверждает позицию в начале строки
#
соответствует символу # буквально (чувствителен к регистру)
Код:
import re
n_rows = 50
path_ = 'your_file_location'
with open(path_,'r') as f:
data = []
for i in range(n_rows): # read only 50 rows here.
for line in f:
if re.match('^#',line):
data.append(line)
start_col = max(enumerate(data))[0]
df = pd.read_csv(path_,sep='s ',skiprows=start_col) # use your actual delimiter.
# Time Cd Cs Cl CmRoll CmPitch
0 0.000005 1.899018 1.491993e-11 2.195012 -0.011086 -1.086380 0.009591
1 0.000010 2.142851 1.004511e-08 2.505163 -0.012652 -1.236757 0.010822
CmYaw Cd(f) Cd(r) Cs(f) Cs(r) Cl(f) Cl(r)
0 0.938423 0.960595 0.009591 -0.009591 0.011126 2.183886 NaN
1 1.058773 1.084078 0.010822 -0.010822 0.015825 2.489338 NaN
Редактировать, обрабатывая #
в имени столбца.
Мы можем сделать это в два этапа. Мы можем читать в 0 строк, но срезать столбец заголовка.
Сначала прочитайте файл из строки заголовка, но установите для header
аргумента значение None
, чтобы заголовки не устанавливались.
Затем мы можем установить заголовки столбцов вручную:
df = pd.read_csv(path_,sep='s ',skiprows=start_col 1, header=None)
df.columns = pd.read_csv(path_,sep='s ',skiprows=start_col,nrows=0).columns[1:]
print(df)
Time Cd Cs Cl CmRoll CmPitch CmYaw
0 0.000005 1.899018 1.491993e-11 2.195012 -0.011086 -1.086380 0.009591
1 0.000010 2.142851 1.004511e-08 2.505163 -0.012652 -1.236757 0.010822
Cd(f) Cd(r) Cs(f) Cs(r) Cl(f) Cl(r)
0 0.938423 0.960595 0.009591 -0.009591 0.011126 2.183886
1 1.058773 1.084078 0.010822 -0.010822 0.015825 2.489338
Комментарии:
1. Это не может обработать
#
тег комментария в строке имени заголовка, который приводит кNaN
в последнем столбце, потому что pandas считает, что инициал#
тоже является именем заголовка. Но ваше решение — хорошее начало для меня. Спасибо.2. @Sunsheep это ваш разделитель на самом деле
's '
?3. ДА. Ваше редактирование работает хорошо, но, на мой взгляд, ответ Стефана немного более элегантный 🙂 Однако эти файлы слишком большие, поэтому ваше решение с чтением только первых 50 строк довольно удобно.
Ответ №4:
Чтобы упростить это и сэкономить время без использования циклов, вы можете создать 2 фрейма данных для #
прокомментированных строк и всего остального. Из этих прокомментированных строк возьмите последнюю — это ваш заголовок, а затем объедините data dataframe и этот заголовок, используя concat()
также, если необходимо назначить первую строку в качестве заголовка, вы можете использовать df.columns=df.iloc[0]
df = pd.DataFrame({
'A':['#test1 : (000000)','#test1 (000000)','#test1 (000000)','#test1 (000000)','#Time (000000)','5e-06','1e-05'],
})
print(df)
A
0 #test1 : (000000)
1 #test1 (000000)
2 #test1 (000000)
3 #test1 (000000)
4 #Time (000000)
5 5e-06
6 1e-05
df_header = df[df.A.str.contains('^#')]
print(df_header)
A
0 #test1 : (000000)
1 #test1 (000000)
2 #test1 (000000)
3 #test1 (000000)
4 #Time (000000)
df_data = df[~df.A.str.contains('^#')]
print(df_data)
A
5 5e-06
6 1e-05
df = (pd.concat([df_header.iloc[[-1]],df_data])).reset_index(drop=True)
df.A=df.A.str.replace(r'^#',"")
print(df)
A
0 Time (000000)
1 5e-06
2 1e-05
Ответ №5:
Предполагается, что комментарии всегда начинаются с одного ‘#’, а заголовок находится в последней прокомментированной строке:
import csv
def read_comments(csv_file):
for row in csv_file:
if row[0] == '#':
yield row.split('#')[1].strip()
def get_last_commented_line(filename):
with open(filename, 'r', newline='') as f:
decommented_lines = [line for line in csv.reader(read_comments(f))]
header = decommented_lines[-1]
skiprows = len(decommented_lines)
return header, skiprows
header, skiprows = get_last_commented_line(path)
pd.read_csv(path, names=header, skiprows=skiprows)
Ответ №6:
# Read the lines in file
with open(file) as f:
lines = f.readlines()
# Last commented line is header
header = [line for line in lines if line.startswith('#')][-1]
# Strip line and remove '#'
header = header[1:].strip().split()
df = pd.read_csv(file, delimiter="s ", names=header, comment='#')