如果在两个数据帧中找到公共键,我想创建一个函数



我有两个数据帧df1和df2,每个数据帧都有包含产品代码和产品价格的列,我想检查2个数据帧中价格之间的差异,并将我创建的这个函数的结果存储在包含产品代码和最终价格的新数据帧"df3"中,这是我的尝试:

以我想要的方式计算差异的函数:

def range_calc(z, y):
final_price = pd.DataFrame(columns = ["Market_price"])
res = z-y
abs_res = abs(res)
if abs_res == 0:
return (z)
if z>y:
final_price = (abs_res / z ) * 100
else:
final_price = (abs_res / y ) * 100
return(final_price)

对于我创建的循环来检查两个 df 并使用该函数:

Last_df = pd.DataFrame(columns = ["Product_number", "Market_Price"])
for i in df1["product_ID"]:
for x in df2["product_code"]:
if i == x:
Last_df["Product_number"] = i
Last_df["Market_Price"] = range_calc(df1["full_price"],df2["tot_price"])

问题是我每次都收到此错误:

错误:序列的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()。

为什么收到错误消息"The truth value of a Series is ambiguous">

您收到错误消息The truth value of a Series is ambiguous因为您尝试在if-子句中输入pandas.Series

nums = pd.Series([1.11, 2.22, 3.33])
if nums == 0:
print("nums  ==  zero (nums is equal to zero)") 
else:
print("nums  !=  zero (nums is not equal to zero)")
# AN EXCEPTION IS RAISED!   

错误消息如下所示:

ValueError: The truth value of a Series is ambiguous. Use a.empty,
a.bool(), a.item(), a.any() or a.all().

不知何故,您对if子句的内部进行了Series

实际上,我知道它是怎么发生的,但我需要一点时间来解释:

好吧,假设您希望将值从熊猫数据帧的第 3 行和第 4 列中取出。

如果您尝试从特定行和列中的 pandas 表中提取单个值,则该值有时是Series对象,而不是数字。

请考虑以下示例:

# DATA:
#    Name  Age  Location
# 0   Nik   31   Toronto
# 1  Kate   30    London
# 2  Evan   40  Kingston
# 3  Kyra   33  Hamilton

要创建上面的数据帧,我们可以编写:

df = pd.DataFrame.from_dict({
'Name': ['Nik', 'Kate', 'Evan', 'Kyra'],
'Age': [31, 30, 40, 33],
'Location': ['Toronto', 'London', 'Kingston', 'Hamilton']
})

现在,让我们尝试获取特定的数据行:

evans_row = df.loc[df['Name'] == 'Evan']

我们尝试从该行数据中获取特定值:

evans_age = evans_row['Age']

你可能认为evans_age是整数40,但你错了。

让我们看看evans_age到底是什么:

print(80*"*", "EVAN's AGE", type(Evans_age), sep="n")
print(Evans_age)

我们有:

EVAN's AGE
<class 'pandas.core.series.Series'>
2    40
Name: Age, dtype: int64

埃文的年龄不是一个数字。evans_age是存储为pandas.Series的类的实例

从 pandas 数据帧中提取单个单元格后,可以写入.tolist()[0]以从该单元格中提取数字。

evans_real_age = evans_age.tolist()[0]
print(80*"*", "EVAN's REAL AGE", type(evans_real_age), sep="n")
print(evans_real_age)
EVAN's REAL AGE
<class 'numpy.int64'>
40

原始代码中的异常可能是由if abs_res == 0引发的。

如果abs_respandas.Seriesabs_res == 0返回另一个系列。

如果整个数字列表等于零,则无法进行比较。

通常人们只在 if 子句中输入一个输入。

if (912):
print("912 is True")
else:
print("912 is False")

当 if 语句收到多个值时,python 解释器不知道该怎么做。

例如,以下应该做什么?

import pandas as pd
data = pd.Series([1, 565, 120, 12, 901])
if data:
print("data is true")
else:
print("data is false")

您应该只在 if 条件中输入一个值。相反,您输入了一个pandas.Series对象作为 if 子句的输入。

在您的情况下,pandas.Series中只有一个数字。但是,通常,pandas.Series包含许多值。

pythonpandas库的作者假设一个序列包含许多数字,即使它只有一个。

计算机认为你厌倦了将许多不同的数字放在一个if子句中。

"函数

定义"和"函数调用"之间的区别

你最初的问题是,

"如果找到公共键,我想创建一个函数">

您对短语">makea function"的使用不正确。 你可能的意思是,"如果找到公共键,我想调用一个函数。

以下是函数">调用"的所有示例:

import pandas as pd
import numpy as np
z = foo(1, 91)
result = funky_function(811, 22, "green eggs and ham")
output = do_stuff()
data = np.random.randn(6, 4)
df = pd.DataFrame(data, index=dates, columns=list("ABCD"))

假设您有两个容器。

如果你真的想在找到公共键时"制作"一个函数,那么你会有如下代码:


dict1 = {'age': 26, 'phone':"303-873-9811"}
dict2 = {'name': "Bob", 'phone':"303-873-9811"}
def foo(dict1, dict2):
union = set(dict2.keys()).intersection(set(dict1.keys()))
# if there is a shared key...
if len(union) > 0:
# make (create) a new function
def bar(*args, **kwargs):
pass
return bar

new_function = foo(dict1, dict2)
print(new_function)

如果不使用def关键字,则称为函数调用

在python中,你用def关键字">制作"一个函数(">定义"一个函数)。

我认为你的问题应该重新命名。

你可以这样写:"如果两个熊猫数据帧有一个公共键,我该如何调用函数?

第二个好问题是,

"如果我们看到错误消息,ValueError: The truth value of a Series is ambiguous.出了什么问题?">

你的问题措辞很奇怪,但我想我可以回答。

<小时 />

生成测试数据

您的问题不包括测试数据。如果您再次询问有关堆栈溢出的问题,请提供一些测试数据的小示例。

以下是我们可以使用的数据示例:

product_ID  full_price
0  prod_id 1-1-1-1       11.11
1  prod_id 2-2-2-2       22.22
2  prod_id 3-3-3-3       33.33
3  prod_id 4-4-4-4       44.44
4  prod_id 5-5-5-5       55.55
5  prod_id 6-6-6-6       66.66
6  prod_id 7-7-7-7       77.77
------------------------------------------------------------
product_code  tot_price
0  prod_id 3-3-3-3      34.08
1  prod_id 4-4-4-4      45.19
2  prod_id 5-5-5-5      56.30
3  prod_id 6-6-6-6      67.41
4  prod_id 7-7-7-7      78.52
5  prod_id 8-8-8-8      89.63
6  prod_id 9-9-9-9     100.74
  • 产品 1 和 2 对于数据框 1 是唯一的
  • 产品 8 和 9 对于数据帧 2 是唯一的
  • 两个数据框都包含产品 3、4、5、...、7 的数据。

数据帧之间的价格略有不同。

上面的测试数据由以下代码生成:

import pandas as pd
from copy import copy
raw_data = [
[
"prod_id {}-{}-{}-{}".format(k, k, k, k),
int("{}{}{}{}".format(k, k, k, k))/100
] for k in range(1, 10)
]
raw_data = [row for row in raw_data]
df1 = pd.DataFrame(data=copy(raw_data[:-2]), columns=["product_ID", "full_price"])
df2 = pd.DataFrame(data=copy(raw_data[2:]), columns=["product_code", "tot_price"])

for rowid in range(0, len(df2.index)):
df2.at[rowid, "tot_price"] += 0.75
print(df1)
print(60*"-")
print(df2)

添加一些错误检查

这被认为是"最佳实践",以确保您的函数 输入格式正确。

您编写了一个名为range_calc(z, y)的函数。我建议确保zy是整数,而不是其他东西(例如熊猫Series对象)。

def range_calc(z, y):
try:
z = float(z)
y = float(y)
except ValueError:
function_name = inspect.stack()[0][3]
with io.StringIO() as string_stream:
print(
"Error: In " + function_name + "(). Inputs should be
like decimal numbers.",
"Instead, we have: " + str(type(y)) + " '" +
repr(str(y))[1:-1] + "'",
file=string_stream,
sep="n"
)
err_msg = string_stream.getvalue()
raise ValueError(err_msg)
# DO STUFF
return

现在我们收到错误消息:

import pandas as pd
data = pd.Series([1, 565, 120, 12, 901])
range_calc("I am supposed to be an integer", data)
# ValueError: Error in function range_calc(). Inputs should be like
decimal numbers.
# Instead, we have: <class 'str'> "I am supposed to be an integer"

代码,实现你想要的。

以下是一些相当丑陋的代码,可以计算您想要的内容:

# You can continue to use your original `range_calc()` function unmodified
# Use the test data I provided earlier in this answer.  
def foo(df1, df2):
last_df = pd.DataFrame(columns = ["Product_number", "Market_Price"])
df1_ids = set(df1["product_ID"].tolist())
df2_ids = set(df2["product_code"].tolist())
pids = df1_ids.intersection(df2_ids) # common_product_ids
for pid in pids:
row1 = df1.loc[df1['product_ID'] == pid]
row2 = df2.loc[df2["product_code"] == pid]
price1 = row1["full_price"].tolist()[0]
price2 = row2["tot_price"].tolist()[0]
price3 = range_calc(price1, price2)
row3  = pd.DataFrame([[pid, price3]], columns=["Product_number", "Market_Price"])
last_df = pd.concat([last_df, row3])
return last_df
# ---------------------------------------
last_df = foo(df1, df2)

结果是:

Product_number  Market_Price
0  prod_id 6-6-6-6      1.112595
0  prod_id 7-7-7-7      0.955171
0  prod_id 4-4-4-4      1.659659
0  prod_id 5-5-5-5      1.332149
0  prod_id 3-3-3-3      2.200704

请注意,我的解决方案丑陋的众多原因之一是以下代码行:

last_df = pd.concat([last_df, row3])

如果last_df很大(数千行),则代码将运行非常缓慢。

这是因为我们没有插入新的数据行,而是:

  1. 复制原始数据帧
  2. 将新数据行追加到副本。
  3. 删除/销毁原始数据框。

复制 10,000 行数据只是为了添加一个新值,然后删除旧的 10,000 行,这真的很愚蠢。

但是,相对而言,我的解决方案的错误比原始代码少。

有时,当您检查序列或数据帧上的条件时,输出是序列,例如 ( , False)。 在这种情况下,您必须使用任何、所有项目,... 使用打印功能根据您的条件查看系列。

另外,我必须告诉您的代码非常非常慢并且具有O(n**2)。您可以先将 df3 计算为连接 df1 和 df2,然后使用应用方法进行快速计算。

相关内容

  • 没有找到相关文章

最新更新