加快使用 .loc 创建新列的 for 循环到更快的方法?
Speed up for-loop that creates a new column using .loc into a faster method?
我一直在使用以下方法根据满足多个列表中的多个条件的每一行为新列分配值。它适用于小型 dfs...但是一旦我处理更大的数据,它就需要很长时间。
for i, j, k in zip(list1, list2, list3):
df.loc[((df['foo'] == i) & (df['bar'] == j)),'new_column'] = k
示例数据:
list1 = ['a', 'a', 'e', 'f', 'c']
list2 = [3, 4, 5, 3, 2]
list3 = ['yellow', 'green', 'blue', 'purple', 'orange', 'black']
df = pd.dataframe({'foo': ['a', 'b', 'c', 'd', 'e', 'f', 'c'],
'bar': [3, 2, 2, 4, 5, 3, 2]})
基本上对于新专栏,我需要:
- 第 1 行标记为黄色(因为 list1=a & list2=3)
- 第 3 行标记为黑色(因为 list1=c & list2=2)
- 第 5 行标记为蓝色(因为 list1=e & list2=5)
- 第 6 行标记为橙色(因为 list1=f & list2=3)
- 第 7 行标记为黑色(因为 list1=c & list2=2)
所有列表的长度都相同。我在 Stack 上搜索了更好的选择,所以我知道一个选择是列表理解(或其他),但不确定如何将它与 .loc 结合起来并创建一个新列。
提前感谢您的帮助!!
您似乎正在尝试在此处实现与联接操作等效的操作。以下应该给你相同的结果并使用数据帧操作,因此可能比循环列表更快。
(我从你的示例中删除了 'purple')
list3 = ['yellow', 'green', 'blue', 'orange', 'black']
根据您的列表构建数据框并加入标准 (foo = i & bar = j):
joiner = pd.DataFrame({"i": list1, "j": list2, "k": list3})
df.join(joiner.set_index(["i", "j"]), on=["foo", "bar"])
给予:
foo bar k
0 a 3 yellow
1 b 2 NaN
2 c 2 black
3 d 4 NaN
4 e 5 blue
5 f 3 orange
6 c 2 black
请注意,如果您有重复的匹配项,则每个 (i, j) 匹配项都会有一个重复的 (foo, bar) 行。您需要对数据帧进行重复数据删除以获得与循环代码相同的结果,并且连接的数据帧可能会变得非常大。我猜虽然因为您的代码总是会覆盖任何重复项,但这种情况并不常见?
想法是将 zip
与 DataFrame
构造函数一起使用,因此可以将 DataFrame.merge
与左连接一起使用:
df1 = pd.DataFrame(zip(list1, list2, list3), columns=['foo','bar','new_column'])
print (df1)
foo bar new_column
0 a 3 yellow
1 a 4 green
2 e 5 blue
3 f 3 purple
4 c 2 orange
df = df.merge(df1, how='left', on=['foo','bar'])
print (df)
foo bar new_column
0 a 3 yellow
1 b 2 NaN
2 c 2 orange
3 d 4 NaN
4 e 5 blue
5 f 3 purple
6 c 2 orange
我一直在使用以下方法根据满足多个列表中的多个条件的每一行为新列分配值。它适用于小型 dfs...但是一旦我处理更大的数据,它就需要很长时间。
for i, j, k in zip(list1, list2, list3):
df.loc[((df['foo'] == i) & (df['bar'] == j)),'new_column'] = k
示例数据:
list1 = ['a', 'a', 'e', 'f', 'c']
list2 = [3, 4, 5, 3, 2]
list3 = ['yellow', 'green', 'blue', 'purple', 'orange', 'black']
df = pd.dataframe({'foo': ['a', 'b', 'c', 'd', 'e', 'f', 'c'],
'bar': [3, 2, 2, 4, 5, 3, 2]})
基本上对于新专栏,我需要:
- 第 1 行标记为黄色(因为 list1=a & list2=3)
- 第 3 行标记为黑色(因为 list1=c & list2=2)
- 第 5 行标记为蓝色(因为 list1=e & list2=5)
- 第 6 行标记为橙色(因为 list1=f & list2=3)
- 第 7 行标记为黑色(因为 list1=c & list2=2)
所有列表的长度都相同。我在 Stack 上搜索了更好的选择,所以我知道一个选择是列表理解(或其他),但不确定如何将它与 .loc 结合起来并创建一个新列。
提前感谢您的帮助!!
您似乎正在尝试在此处实现与联接操作等效的操作。以下应该给你相同的结果并使用数据帧操作,因此可能比循环列表更快。
(我从你的示例中删除了 'purple')
list3 = ['yellow', 'green', 'blue', 'orange', 'black']
根据您的列表构建数据框并加入标准 (foo = i & bar = j):
joiner = pd.DataFrame({"i": list1, "j": list2, "k": list3})
df.join(joiner.set_index(["i", "j"]), on=["foo", "bar"])
给予:
foo bar k
0 a 3 yellow
1 b 2 NaN
2 c 2 black
3 d 4 NaN
4 e 5 blue
5 f 3 orange
6 c 2 black
请注意,如果您有重复的匹配项,则每个 (i, j) 匹配项都会有一个重复的 (foo, bar) 行。您需要对数据帧进行重复数据删除以获得与循环代码相同的结果,并且连接的数据帧可能会变得非常大。我猜虽然因为您的代码总是会覆盖任何重复项,但这种情况并不常见?
想法是将 zip
与 DataFrame
构造函数一起使用,因此可以将 DataFrame.merge
与左连接一起使用:
df1 = pd.DataFrame(zip(list1, list2, list3), columns=['foo','bar','new_column'])
print (df1)
foo bar new_column
0 a 3 yellow
1 a 4 green
2 e 5 blue
3 f 3 purple
4 c 2 orange
df = df.merge(df1, how='left', on=['foo','bar'])
print (df)
foo bar new_column
0 a 3 yellow
1 b 2 NaN
2 c 2 orange
3 d 4 NaN
4 e 5 blue
5 f 3 purple
6 c 2 orange