Как создать фрейм данных, импортировав данные из нескольких файлов .csv, имеющих одинаковое содержимое? - PullRequest
3 голосов
/ 01 августа 2020

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

Фон

Я пытаюсь автоматизировать манипуляции с данными для своей исследовательской лаборатории в школе с помощью python. В результате эксперимента будет создан файл .csv, содержащий 41 строку данных без заголовка, как показано ниже.

enter image description here

Sometimes, multiple runs of the same experiment exist and that will produce .csv files with the same header, and taking an average of them is needed for accuracy. Something like this with the same number of rows and headers:

enter image description here

So far I was able to filter the basenames to only contain the .csv files of the same parameters and have them added to a data frame. However, my issue is that I don't know how to continue to get an average.

My Current Code and output

Code:

import pandas as pd
import os

dir = "/Users/luke/Desktop/testfolder"

files = os.listdir(dir)
files_of_interests = {}

for filename in files:
    if filename[-4:] == '.csv':
        key = filename[:-5]
        files_of_interests.setdefault(key, [])
        files_of_interests[key].append(filename)

print(files_of_interests)

for key in files_of_interests:
    stack_df = pd.DataFrame()
    print(stack_df)
    for filename in files_of_interests[key]:
        stack_df = stack_df.append(pd.read_csv(os.path.join(dir, filename)))
    print(stack_df)

Output:

Empty DataFrame
Columns: []
Index: []
    Unnamed: 0  Wavelength       S2c  Wavelength.1        S2
0            0        1100  0.000342          1100  0.000304
1            1        1110  0.000452          1110  0.000410
2            2        1120  0.000468          1120  0.000430
3            3        1130  0.000330          1130  0.000306
4            4        1140  0.000345          1140  0.000323
..         ...         ...       ...           ...       ...
36          36        1460  0.002120          1460  0.001773
37          37        1470  0.002065          1470  0.001693
38          38        1480  0.002514          1480  0.002019
39          39        1490  0.002505          1490  0.001967
40          40        1500  0.002461          1500  0.001891

[164 rows x 5 columns]

Question Here!

So my question is, how do I get it to append towards the right individually for each S2c and S2?

Explanation:

With multiple .csv files with the same header names, when I append it to the list it just keeps stacking towards the bottom of the previous .csv file which led to the [164 rows x 5 columns] from the previous section. My original idea is to create a new data frame and only appending S2c and S2 from each of those .csv files such that instead of stacking on top of one another, it will keep appending them as new columns towards the right. Afterward, I can do some form of pandas column manipulation to have them added and divided by the number of runs (which are just the number of files, so len(files_of_interests[key]) under the second FOR loop).

What I have tried

  1. I have tried creating an empty data frame and adding a column that is taken from np.arange(1100,1500,10) using pd.DataFrame.from_records(). And append S2c and S2 to the data frame as I have described from the previous section. The same issue occurred, in addition to that, it produces a bunch of Nan values which I am not too well equipped to deal with even after searching further.

  2. I have read up on multiple other questions posted here, many suggested using pd.concat but since the answers are tailored to a different situation, I can't really replicate it nor do was I able to understand the documentation for it so I stopped pursuing this path.

Thank you in advance for your help!

Additional Info

I am using macOS and ATOM for the code.

The csv files can be found here!

github: https://github.com/teoyi/PROJECT-Automate-Research-Process

Опробование метода @zabop

Код:

dflist = []
for key in files_of_interests:
    for filename in files_of_interests[key]:
        dflist.append(pd.read_csv(os.path.join(dir, filename)) )
concat = pd.concat(dflist, axis = 1)
concat.to_csv(dir + '/concat.csv')

Вывод:

enter image description here

Trying @SergeBallesta method

Code:

df = pd.concat([pd.read_csv(os.path.join(dir, filename))
                for key in files_of_interests for filename in files_of_interests[key]])

df = df.groupby(['Unnamed: 0', 'Wavelength', 'Wavelength.1']).mean().reset_index()
df.to_csv(dir + '/try.csv')
print(df)

Output:

введите описание изображения здесь

Ответы [ 3 ]

1 голос
/ 01 августа 2020

IIU C у вас есть:

  • куча файлов csv, каждый из которых содержит результат того же эксперимента
  • первый соответствующий столбец всегда содержит числа от 0 до 40 (так что в каждом файле 41 строка)
  • столбцы Wavelenght и Wavelength.1 всегда содержат одинаковые значения от 1100 до 1500 с шагом 10
  • , но дополнительные столбцы могут существуют перед первым подходящим
  • первый столбец не имеет имени в CSV-файле, а до первого соответствующего имена начинаются с 'Unnamed: '

и вы хотите получить средние значения столбцов S2 и S2 c для одного и того же значения длины волны.

Это можно сделать просто с помощью groupby и mean, но сначала мы должны отфильтровать все ненужные столбцы. Это можно сделать с помощью параметра index_col и usecols в read_csv:

...
print(files_of_interests)

# first concat the datasets:
dfs = [pd.read_csv(os.path.join(dir, filename), index_col=1,
                   usecols=lambda x: not x.startswith('Unnamed: '))
       for key in files_of_interests for filename in files_of_interests[key]]
df = pd.concat(dfs).reset_index()

# then take the averages
df = df.groupby(['Wavelength', 'Wavelength.1']).mean().reset_index()

# reorder columns and add 1 to the index to have it to run from 1 to 41
df = df.reindex(columns=['Wavelength', 'S2c', 'Wavelength.1', 'S2'])
df.index += 1

Если в результирующем df все еще есть ненужные столбцы , эта команда magi c поможет идентифицировать исходные файлы со странной структурой:

import pprint

pprint.pprint([df.columns for df in files])

С файлами из github testfolder, он дает:

[Index(['Unnamed: 0', 'Wavelength', 'S2c', 'Wavelength.1', 'S2'], dtype='object'),
 Index(['Unnamed: 0', 'Wavelength', 'S2c', 'Wavelength.1', 'S2'], dtype='object'),
 Index(['Unnamed: 0', 'Wavelength', 'S2c', 'Wavelength.1', 'S2'], dtype='object'),
 Index(['Unnamed: 0', 'Wavelength', 'S2c', 'Wavelength.1', 'S2'], dtype='object'),
 Index(['Unnamed: 0', 'Unnamed: 0.1', 'Wavelength', 'S2c', 'Wavelength.1',
       'S2'],
      dtype='object'),
 Index(['Unnamed: 0', 'Wavelength', 'S2c', 'Wavelength.1', 'S2'], dtype='object')]

Это дает понять, что пятый файл в качестве дополнительных столбцов.

1 голос
/ 01 августа 2020

Если у вас есть список фреймов данных, например:

import pandas as pd
data = {'col_1': [3, 2, 1, 0], 'col_2': [3, 1, 2, 0]}
dflist = [pd.DataFrame.from_dict(data) for _ in range(5)]

Вы можете сделать:

pd.concat(dflist,axis=1)

Что будет выглядеть так:

enter image description here

If you want to append each column name with a number indicating which df they came from, before concat, do:

for index, df in enumerate(dflist):
    df.columns = [col+'_'+str(index) for col in df.columns]

Then pd.concat(dflist,axis=1), resulting:

введите описание изображения здесь

Хотя я не могу воспроизвести вашу файловую систему и подтвердить, что это работает, для создания dflist из ваших файлов должно работать что-то вроде этого:

dflist = []
for key in files_of_interests:
    print(stack_df)
    for filename in files_of_interests[key]:
        dflist.append( pd.read_csv(os.path.join(dir, filename)) )
        
0 голосов
/ 02 августа 2020

Оказалось, что и @zabop, и @SergeBallesta предоставили мне ценную информацию для работы над этой проблемой через pandas.

То, что я хотел:

  1. Соответствующие столбцы S2 c и S2 каждого файла в парах ключ: значение должны быть объединены в один .csv файл для дальнейшей обработки.

  2. Удалить избыточные столбцы только для показать один столбец Wavelength, который находится в диапазоне от 1100 до 1500 с шагом 10.

Это требует использования pd.concat, которое было введено @zabop и @SergeBallesta как показано ниже:

for key in files_of_interests:
    list = []
    for filename in files_of_interests[key]:
        list.append(pd.read_csv(os.path.join(dir,filename)))
        df = pd.concat(list, axis = 1)
        df = df.drop(['Unnamed: 0', 'Wavelength.1'], axis = 1)
        print(df)
        df.to_csv(os.path.join(dir + '/', f"{filename[:-5]}_master.csv"))

Мне пришлось использовать files_of_interests[key], чтобы он мог читать имена файлов, и pd.read_csv, чтобы прочитать правильный путь. Помимо этого, я добавил axis = 1 к pd.concat, что позволяет объединять его по горизонтали вместе с циклами for для правильного доступа к именам файлов. (Я дважды проверил значения, и они совпадают с соответствующими файлами .csv.)

Вывод .csv выглядит следующим образом:

введите описание изображения здесь

Единственная проблема сейчас в том, что groupby, предложенный @SergeBallesta, не работает, поскольку возвращает ValueError: Grouper for 'Wavelength' not 1-dimensional. Я создам новый вопрос для этого, если к концу дня не добьюсь прогресса.

Еще раз большое спасибо @zabop и @SergeBallesta за попытку, хотя мое объяснение не было таким: Слишком ясно, их ответы определенно дали мне столь необходимое понимание того, как работает pandas.

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