У меня есть сценарий для загрузки и объединения данных временных рядов из двух файлов .csv, имеющих одинаковое базовое имя файла (указанное как путь с помощью Pathlib
), но разные суффиксы. Учитывая, что импорт данных из файлов .csv
работает правильно, я привожу сюда минимальный рабочий пример с данными, предоставленными с использованием StringIO
:
import pandas as pd
from io import StringIO
def load_data():
headers_0 = ['a', 'b', 'c'] # Headers for first file. May have more entries than columns in file
headers_1 = ['d', 'e'] # Headers for second file.
data_0 = pd.read_csv(
StringIO(
"""
0 1 2
3 4 5
6 7 8
"""
)
, header=None, delim_whitespace=True)
data_0.columns = headers_0[0:data_0.shape[1]]
data_1 = pd.read_csv(
StringIO(
"""
A B
C D
"""
)
, header=None, delim_whitespace=True)
data_1.columns = headers_1[0:data_1.shape[1]]
data = data_0.join(data_1)
data.fillna(0, inplace=True)
print(data)
До сих пор я использовал load_data
только для наборов данных, где data_0
и data_1
имеют одинаковую длину столбцов (одинаковую длину временного ряда). Однако сейчас я сталкиваюсь с ситуацией, когда длина столбца data_1
короче, чем data_0
; это потому, что данные в data_1
начинают записываться позже, чем в data_0
.
Как использовать pandas для заполнения столбцов data_1
ведущими нулями так, чтобы длина столбца как в data_0
, так и в data_1
была одинаковой? Я считаю, что строка data.fillna(0, inplace=True)
заполняет несоответствие длины конечными нулями; есть ли очевидный способ изменить это на ведущие нули? Обратите внимание, что я априори не знаю длину ни одного набора данных, поэтому я был бы признателен за помощь в поиске решения, которое работает на основе длины данных, загруженных с помощью pandas. Выполнение приведенного выше примера дает результат
a b c d e
0 0 1 2 A B
1 3 4 5 C D
2 6 7 8 0 0
что, очевидно, не является желаемым эффектом (нули в столбцах d
и e
будут в строке 0, а не в строке 2).
Я пробовал разные варианты DataFrame.fillna
, например method=backfill
, но ни одна из этих попыток не дала ожидаемого результата.
На самом деле это не минимальный рабочий пример, поскольку в нем отсутствуют данные примера (входные данные, желаемый результат и фактический результат). Вы также должны показать, как вызывается функция. Или, если синтаксический анализ CSV работает отлично, просто укажите сами data_0
и data_1
(то есть примеры версий). См. Как сделать хорошие воспроизводимые примеры панд.
Извиняюсь, @wjandrea, я добавлю необходимые данные, чтобы сделать их минимальным рабочим примером, когда в следующий раз получу доступ к своему компьютеру.
Вот один из подходов:
dfs
с помощью [::-1]
и примените df.reset_index с drop=True
axis=1
и снова поменяйте местами + reset_index
.import pandas as pd
from io import StringIO
def load_data(filename):
headers_0 = ['a', 'b', 'c']
headers_1 = ['d', 'e']
# using StringIO for mre
# + `sep = "\s+"` for `delim_whitespace=True` (deprecated since 2.2.0)
data_0 = pd.read_csv(StringIO(c_0), header=None, sep = "\s+")
data_0.columns = headers_0[0:data_0.shape[1]]
data_1 = pd.read_csv(StringIO(c_1), header=None, sep = "\s+")
data_1.columns = headers_1[0:data_1.shape[1]]
data = (
pd.concat(
[df[::-1].reset_index(drop=True) for df in [data_0, data_1]],
axis=1
)[::-1]
.reset_index(drop=True)
.fillna(0)
)
return data
c_0 = """0 1 2
3 4 5
6 7 8"""
c_1 = """A B
C D
"""
load_data('c')
a b c d e
0 0 1 2 0 0
1 3 4 5 A B
2 6 7 8 C D
Будет работать независимо от того, какой из них длиннее.
Другой вариант — использовать df.shift с fill_value
:
def load_data2(filename):
# `data_0` and `data_1` same as above
diff = len(data_0) - len(data_1)
data = pd.concat([data_0, data_1], axis=1)
if diff > 0:
data[data_1.columns] = data[data_1.columns].shift(diff, fill_value=0)
elif diff < 0:
data[data_0.columns] = data[data_0.columns].shift(abs(diff), fill_value=0)
return data
Преимущество этой опции в том, что она заполняет только значения NaN
в смещенной области. Например.:
c_0 = """0 1 2
3 4 5
6 7 8"""
c_1 = """A B
C
"""
load_data2('c')
a b c d e
0 0 1 2 0 0
1 3 4 5 A B
2 6 7 8 C NaN # with my `load_data` above, this `NaN` gets filled too!
Конечно, если вы все равно хотите, чтобы все значения NaN
были заполнены, просто используйте fillna
и здесь.
Спасибо за ответ @ouroboros1! Из вашего примера выполнения кода выходит, что это добавление нулей после данных c_1, а не перед ними? Я ожидаю, что нули в столбцах «d» и «e» появятся первыми?
Да, моя вина. Обновлено :)
Спасибо! Я отметил это как ответ, учитывая, что ваше первое решение является наиболее компактным и требует наименьшего количества if/then
операторов.
Я не особо разбираюсь в пандах, мне удалось найти решение по-своему:
import pandas as pd
from pathlib import Path
def load_data(filename):
headers_0 = ['a', 'b', 'c']
headers_1 = ['d', 'e']
data_0 = pd.read_csv(str(filename.with_suffix('')) + '_0.csv', header=None, delim_whitespace=True)
data_0.columns = headers_0[0:data_0.shape[1]]
data_1 = pd.read_csv(str(filename.with_suffix('')) + '_1.csv', header=None, delim_whitespace=True)
data_1.columns = headers_1[0:data_1.shape[1]]
len_0 = len(data_0)
len_1 = len(data_1)
if len_0 > len_1:
zeros_to_prepend = pd.DataFrame(0, index=range(len_0 - len_1), columns=data_1.columns)
data_1 = pd.concat([zeros_to_prepend, data_1], ignore_index=True)
data = data_0.join(data_1)
return data
filename = Path('filename/') # I didn't really understand what this was for, so just entered filename
data = load_data(filename)
print(data)
Это был мой результат:
a b c d e
0 1 2 3 0 0
1 4 5 6 0 0
2 7 8 9 100 200
3 10 11 12 300 400
При использовании pandas у меня также было несколько предупреждений. В нем говорилось, что ключевое слово, используемое в пандах, устарело. Я использую Linux, поэтому с моей стороны могут возникнуть проблемы!
«Ключевое слово, используемое в пандах, устарело» — Какое? delim_whitespace
фактически устарел; вы можете проверить документы.
@wjandrea Ох, ок. Спасибо за информацию. Но подождите, они изменили это sep='\s+', если я правильно прочитал. Хотя это так излишне сложно без всякой причины.
О, вы не знаете о слиянии? См. Слияние панд 101