我有一个数据帧df1。 “交易”列具有一个整数数组。
id transactions
1 [1,2,3]
2 [2,3]
数据帧df2。 “ items”列具有一个int数组。
items cost
[1,2] 2.0
[2] 1.0
[2,4] 4.0
如果需要汇总费用,我需要检查项目的所有元素是否都在每次交易中。
预期结果
id transaction score
1 [1,2,3] 3.0
2 [2,3] 1.0
我做了以下
#cross join
-----------
def cartesian_product_simplified(left, right):
la, lb = len(left), len(right)
ia2, ib2 = np.broadcast_arrays(*np.ogrid[:la,:lb])
return pd.DataFrame(
np.column_stack([left.values[ia2.ravel()],
right.values[ib2.ravel()]]))
out=cartesian_product_simplified(df1,df2)
#column names assigning
out.columns=['id', 'transactions', 'cost', 'items']
#converting panda series to list
t=out["transactions"].tolist()
item=out["items"].tolist()
#check list present in another list
-------------------------------------
def check(trans,itm):
out_list=list()
for row in trans:
ret =np.all(np.in1d(itm, row))
out_list.append(ret)
return out_list
if true: group and sum
-----------------------
a=check(t,item)
for i in a:
if(i):
print(out.groupby(['id','transactions']))['cost'].sum()
else:
print("no")
引发TypeError:'NoneType'对象不可下标。
我是python的新手,不知道如何将所有这些放在一起。当一个列表的所有项目都在另一列表中时,如何对成本进行分组和求和?
答案 0 :(得分:3)
最简单的方法就是检查所有交易的所有项目:
# df1 and df2 are initialized
def sum_score(transaction):
score = 0
for _, row in df2.iterrows():
if all(item in transaction for item in row["items"]):
score += row["cost"]
return score
df1["score"] = df1["transactions"].map(sum_score)
大规模运行将非常缓慢。如果这是一个问题,我们不需要遍历每个项目,而只能预选可能的项目。如果您有足够的内存,可以这样做。对于每个项目,我们都记住它出现在df2
中的所有行号。因此,对于每笔交易,我们都会得到项目,获得所有可能的行并仅检查它们。
import collections
# df1 and df2 are initialized
def get_sum_score_precalculated_func(items_cost_df):
# create a dict of possible indexes to search for an item
items_search_dict = collections.default_dict(set)
for i, (_, row) in enumerate(items_cost_df.iterrow()):
for item in row["items"]:
items_search_dict[item].add(i)
def sum_score(transaction):
possible_indexes = set()
for i in transaction:
possible_indexes += items_search_dict[i]
score = 0
for i in possible_indexes:
row = items_cost_df.iloc[i]
if all(item in transaction for item in row["items"]):
score += row["cost"]
return score
return sum_score
df1["score"] = df1["transactions"].map(get_sum_score_precalculated_func(df2))
我在这里使用
set
,这是唯一值的无序存储(它有助于连接可能的行号并避免重复计数)。
collections.defaultdict
,这是通常的dict
,但是如果您尝试访问未初始化的值,则会使用给定的数据填充它(在我的情况下为空白set
)。有助于避免if x not in my_dict: my_dict[x] = set()
。我也使用所谓的“关闭”,这意味着sum_score
函数将可以访问items_cost_df
函数声明后的级别上可以访问的items_search_dict
和sum_score
已返回,并且get_sum_score_precalculated_func
万一这些项目非常独特并且只能在几行df2
中找到,那应该快得多。
如果您有很多独特商品,并且有很多相同的交易,则最好先计算每笔独特交易的得分。然后只需加入结果即可。
transactions_score = []
for transaction in df1["transactions"].unique():
score = sum_score(transaction)
transaction_score.append([transaction, score])
transaction_score = pd.DataFrame(
transaction_score,
columns=["transactions", "score"])
df1 = df1.merge(transaction_score, on="transactions", how="left")
在这里,我使用第一个代码示例中的sum_score
P.S。对于python错误消息,应该有一个行号,这对理解问题有很大帮助。
答案 1 :(得分:1)
# convert df_1 to dictionary for iteration
df_1_dict = dict(zip(df_1["id"], df_1["transactions"]))
# convert df_2 to list for iteration as there is no unique column
df_2_list = df_2.values.tolist()
# iterate through each combination to find a valid one
new_data = []
for rows in df_2_list:
items = rows[0]
costs = rows[1]
for key, value in df_1_dict.items():
# find common items in both
common = set(value).intersection(set(items))
# execute of common item exist in second dataframe
if len(common) == len(items):
new_row = {"id": key, "transactions": value, "costs": costs}
new_data.append(new_row)
merged_df = pd.DataFrame(new_data)
merged_df = merged_df[["id", "transactions", "costs"]]
# group the data by id to get total cost for each id
merged_df = (
merged_df
.groupby(["id"])
.agg({"costs": "sum"})
.reset_index()
)