#python #pandas #dataframe #dictionary
Вопрос:
У меня есть следующий словарь:
rates = {'USD':
{'2019': 1,
'2020': 2,
'2021': 3},
'CAD':
{'2019': 4,
'2020': 5,
'2021': 6}
}
и у меня есть следующий фиктивный фрейм данных:
Item Currency Year Rate
0 1 USD 2019
1 2 USD 2020
2 3 CAD 2021
3 4 CAD 2019
4 5 GBP 2020
Теперь я хочу заполнить столбец Rate
, сопоставив правильную скорость, где rate = f(currency,year)
. Я пытаюсь с:
def map_rate(data, rates):
for index, row in data.iterrows():
currency = str(row['Currency'])
if currency in list(rates.keys()):
year = str(row['Year'])
rate = rates[currency][year]
else:
rate = 1
return rate
Я использую вышесказанное следующим образом:
df['Rate'] = map_rate(test, rates)
Однако это возвращает только первую ставку, например значение 1, вместо соответствующих ставок:
Item Currency Year Rate
0 1 USD 2019 1
1 2 USD 2020 1
2 3 CAD 2021 1
3 4 CAD 2019 1
4 5 GBP 2020 1
Ожидаемый результат таков:
Item Currency Year Rate
0 1 USD 2019 1
1 2 USD 2020 2
2 3 CAD 2021 6
3 4 CAD 2019 4
4 5 GBP 2020 1
В чем моя ошибка?
Комментарии:
1. Примечание: вы можете напрямую проверить по словарю, существует ли ключ или нет:
if currency in rates
вместоif currency in list(rates.keys())
. Последний формирует список и теряет ~O(1) времени поиска.
Ответ №1:
Вот один из способов: создайте многоиндексный ряд из ставок stack
, reindex
используя значения из df , чтобы получить желаемую ставку за строку.
df['rate'] = (
pd.DataFrame(rates)
.stack()
.reindex(pd.MultiIndex.from_frame(df[['Year','Currency']].astype(str)),
fill_value=1)
.to_numpy()
)
print(df)
Item Currency Year rate
0 1 USD 2019 1
1 2 USD 2020 2
2 3 CAD 2021 6
3 4 CAD 2019 4
4 5 GBP 2020 1
Ответ №2:
С помощью .apply
Экс:
df['Rate'] = df.apply(lambda x: rates[x['Currency']][x['Year']], axis=1)
# OR
df['Rate'] = df.apply(lambda x: rates.get(x['Currency'], dict()).get(x['Year'], 1), axis=1)
print(df)
Выход:
Item Currency Year Rate
0 1 USD 2019 1
1 2 USD 2020 2
2 3 CAD 2021 6
3 4 CAD 2019 4
4 5 GBP 2020 1
Комментарии:
1. Спасибо. Как вы думаете, почему мое решение не работает?
2. Я не уверен, почему это не работает….это, вероятно, потому
if currency in list(rates.keys()):
, что не удается3. Я все равно получу значение
1
, даже если я удалю условие. Похоже, что он неправильно зацикливается на разных названиях валют.4. @Zizzipupp это потому, что ваша функция возвращает скаляр (и в данном случае 1, потому что это последнее значение, полученное в цикле), в то время как вы должны возвращать список значений, по одному для каждой итерации
5. У вас, вероятно, плохие данные. проверьте, есть ли в нем ведущее место?
Ответ №3:
Создайте другой фрейм данных для ставок
rates_df = pd.DataFrame(rates).unstack().reset_index()
rates_df.columns = ['Currency', 'Year', 'Rates']
rates_df['Year'] = rates_df['Year'].astype(int)
Затем объедините
df.merge(rates_df, on=['Currency', 'Year'], how='left').fillna(1)
Фрейм данных ставок
Currency Year Rates
0 USD 2019 1
1 USD 2020 2
2 USD 2021 3
3 CAD 2019 4
4 CAD 2020 5
5 CAD 2021 6
Выход
Item Currency Year Rates
0 1 USD 2019 1.0
1 2 USD 2020 2.0
2 3 CAD 2021 6.0
3 4 CAD 2019 4.0
4 5 GBP 2020 1.0
Ответ №4:
Это можно легко сделать с помощью встроенного метода Pandas df.apply()
. Вот более подробный пример, чем другие опубликованные ответы.
Код:
def get_rate(row):
if row['Currency'] in rates.keys():
return rates[row['Currency']][row['Year']]
else:
return 1
df['Rate'] = df.apply(get_rate,axis=1)
print(df)