下面是示例数据框
>>> df = pd.DataFrame({'a': [1, 1, 1, 2, 2], 'b':[11, 22, 33, 44, 55]})
>>> df
a b
0 1 11
1 1 22
2 1 33
3 2 44
4 3 55
现在我想根据索引从其他字典更新/替换与某列匹配的b值
例如:
match = {1:[111, 222], 2:[444, 555]}
输出:
a b
0 1 111
1 1 222
2 1 33 <-- ignores this bcz not enough values to replace in match dict for 1
3 2 444
4 3 555
预先感谢
答案 0 :(得分:4)
您可以使用列表的弹出功能:
import pandas as pd
def pop(default, lst):
try:
return lst.pop()
except IndexError:
return default
df = pd.DataFrame({'a': [1, 1, 1, 2, 2], 'b': [11, 22, 33, 44, 55]})
match = {1: [111, 222], 2: [444, 555]}
df['b'] = df[['a', 'b']].apply(lambda e: pop(e[1], match[e[0]]), axis=1)
print(df)
输出
a b
0 1 222
1 1 111
2 1 33
3 2 555
4 2 444
如果必须保留订单,则始终可以弹出第一项:
def pop(default, lst):
try:
return lst.pop(0)
except IndexError:
return default
输出
a b
0 1 111
1 1 222
2 1 33
3 2 444
4 2 555
更新
一种更快(无损)的方法是使用deque:
def pop(default, lst):
try:
return lst.popleft()
except IndexError:
return default
match_deque = {k: deque(v[:]) for k, v in match.items()}
df['b'] = df[['a', 'b']].apply(lambda e: pop(e[1], match_deque[e[0]]), axis=1)
print(df)
答案 1 :(得分:4)
这是一种方式。想法是按组计算累积计数,并用它来过滤行。使用itertools.chain
创建单个值数组。最后,使用pd.DataFrame.loc
和布尔索引来设置值。
from itertools import chain
count = df.groupby('a').cumcount() + 1
m1 = df['a'].isin(match)
m2 = count.le(df['a'].map(match).map(len))
values = list(chain.from_iterable(match.values()))
df.loc[m1 & m2, 'b'] = values
print(df)
a b
0 1 111
1 1 222
2 1 33
3 2 444
4 2 555