#pandas #dataframe
#pandas #фрейм данных
Вопрос:
У меня есть 2 фрейма данных:
siccd date retp
0 2892 31135 -0.036296
1 2892 31226 0.144768
2 2892 31320 0.063924
3 1650 31412 -0.009190
4 1299 31502 0.063326
и
start end ind indname
0 100 999 1 Agric
1 1000 1299 2 Mines
2 1300 1399 3 Oil
3 1400 1499 4 Stone
4 1500 1799 5 Cnstr
5 2000 2099 6 Food
6 2100 2199 7 Smoke
7 2200 2299 8 Txtls
8 2300 2399 9 Apprl
9 2400 2499 10 Wood
10 2500 2599 11 Chair
11 2600 2661 12 Paper
12 2700 2799 13 Print
13 2800 2899 14 Chems
14 2900 2999 15 Ptrlm
15 3000 3099 16 Rubbr
16 3100 3199 17 Lethr
17 3200 3299 18 Glass
18 3300 3399 19 Metal
Задача состоит в том, чтобы взять df1['siccd']
столбец, сравнить его со столбцом df2['start']
и df2['end']
. Если (start <= siccd <= end), присвоите значения ind и indname соответствующего индекса во втором фрейме данных первому фрейму данных. Результат будет выглядеть примерно так:
siccd date retp ind indname
0 2892 31135 -0.036296 14 Chems
1 2892 31226 0.144768 14 Chems
2 2892 31320 0.063924 14 Chems
3 1650 31412 -0.009190 5 Cnstr
4 1299 31502 0.063326 2 Mines
Я пытался сделать это с помощью грубых вложенных циклов for , и это дает мне правильные списки, которые я могу добавить в конец фрейма данных, однако это крайне неэффективно, и, учитывая длину набора данных, этого недостаточно.
siccd_lst = list(tmp['siccd'])
ind_lst = []
indname_lst = []
def categorize(siccd, df, index):
if (siccd >= df.iloc[index]['start']) and (siccd <= df.iloc[index]['end']):
ind_lst.append(df.iloc[index]['ind'])
indname_lst.append(df.iloc[index]['indname'])
else:
pass
for i in range(0, len(ff38.index)-1):
[categorize(x, ff38, i) for x in siccd_lst]
Я также попытался векторизовать проблему, однако я не мог понять, как выполнить итерацию по всему df2 при «поиске» правильного ind и indname для присвоения первому фрейму данных.
Комментарии:
1. Я вырезал один из дополнительных столбцов, которые не имеют отношения к этому, но df1.shape выводит (413840, 21) и df2.shape выводит (41, 4). Спасибо!
2. Вы правы! Это было неправильно, я отредактировал его. Спасибо.
Ответ №1:
Интервалы
Мы создадим фрейм данных, где индекс — это интервал, а столбцы — значения, которые мы хотим сопоставить, затем мы можем использовать .loc
этот фрейм данных для переноса данных.
Если какое-либо из ваших 'siccd'
значений лежит за пределами всех интервалов, вы получите a KeyError
, поэтому этот метод не будет работать.
dfi = pd.DataFrame({'indname': df2['indname'].to_numpy(), 'ind': df2['ind'].to_numpy()},
index=pd.IntervalIndex.from_arrays(left=df2.start, right=df2.end, closed='both'))
df1[['indname', 'ind']] = dfi.loc[df1.siccd].to_numpy()
Слияние
Вы можете выполнить полное слияние (все строки в df1 со всеми строками в df2), используя временный столбец ( 't'
), а затем отфильтровать результат там, где он находится между значениями.
Поскольку ваш второй фрейм данных, по-видимому, имеет небольшое количество неперекрывающихся диапазонов, результат слияния не должен быть чрезмерно большим с точки зрения объема памяти, а неперекрывающиеся диапазоны гарантируют, что в результате фильтрации останется не более одной строки для каждой исходной строки в df1.
Если какое-либо из ваших 'siccd'
значений лежит за пределами всех интервалов, строка из исходного фрейма данных будет удалена.
res = (df1.assign(t=1)
.merge(df2.assign(t=1), on='t', how='left')
.query('siccd >= start amp; siccd <= end')
.drop(columns=['t', 'start', 'end']))
# siccd date retp ind indname
#13 2892 31135 -0.036296 14 Chems
#32 2892 31226 0.144768 14 Chems
#51 2892 31320 0.063924 14 Chems
#61 1650 31412 -0.009190 5 Cnstr
#77 1299 31502 0.063326 2 Mines
Если вы ожидаете, что значения будут находиться за пределами некоторых интервалов, измените слияние. Приведите исходный индекс subset, который удаляет эти строки и использует combine_first
их для добавления обратно после слияния. Я добавил строку в конце с символом 'siccd'
of 252525
в качестве 6-й строки к вашему оригиналу df1
:
res = (df1.reset_index().assign(t=1)
.merge(df2.assign(t=1), on='t', how='left')
.query('siccd >= start amp; siccd <= end')
.drop(columns=['t', 'start', 'end'])
.set_index('index')
.combine_first(df1) # Adds back rows, based on index,
) # that were outside any Interval
# date ind indname retp siccd
#0 31135.0 14.0 Chems -0.036296 2892.0
#1 31226.0 14.0 Chems 0.144768 2892.0
#2 31320.0 14.0 Chems 0.063924 2892.0
#3 31412.0 5.0 Cnstr -0.009190 1650.0
#4 31502.0 2.0 Mines 0.063326 1299.0
#5 31511.0 NaN NaN 0.151341 252525.0