我有一个这种类型的MultiIndex pandas DataFrame:
import pandas as pd
df_multi = pd.DataFrame([['A', 'A1', 0,234,2002],['A', 'A1', 1,324,2550],
['A', 'A1', 2,345,3207],['A', 'A1', 3,458,4560],['A', 'A2', 0,569,1980],
['A', 'A2', 1,657,2314],['A', 'A2', 2,768,4568],['A', 'A2', 3,823,5761]],
columns=['Product','Scenario','Time','Quantity','Price']).set_index(
['Product', 'Scenario'])
和这种类型的单个索引DataFrame:
df_single = pd.DataFrame([['A', -3,100],['A', -2,100], ['A', -1,100]],
columns=['Product','Time','Quantity']).set_index(['Product'])
对于df_multi的第一个索引级别中的每个“Product”,以及第二个级别中的每个“Scenario”,我想追加/连接df_single中的行,其中包含一些要附加的负“时间”值在df_multi中的正“时间”值开始之前。
我还希望得到的DataFrame首先由['Product','Scenario']进行MultiIndexed(就像df_multi),其次是按'Time'的递增值排序的行。换句话说,期望的结果是:
df_result = pd.DataFrame([['A', 'A1', -3,100,'NaN'],['A', 'A1', -2,100,'NaN'],
['A', 'A1', -1,100,'NaN'],['A', 'A1', 0,234,2002],['A', 'A1', 1,324,2550],
['A', 'A1', 2,345,3207],['A', 'A1', 3,458,4560],['A','A2', -3,100,'NaN'],
['A', 'A2', -2,100,'NaN'],['A', 'A2', -1,100,'NaN'],['A', 'A2', 0,569,1980],
['A', 'A2', 1,657,2314],['A', 'A2', 2,768,4568],['A', 'A2', 3,823,5761]],
columns=['Product','Scenario','Time','Quantity','Price']).set_index(
['Product', 'Scenario'])
编辑:df_single没有'Scenario'值,这可能令人困惑。只要“Product”匹配,df_single的相同行将附加到df_multi中的每个场景,并且它们只是“继承”Scenario值的免费。
我正在使用的实际DataFrame相当大(几千'产品',每个产品几千'方案',每个方案几百'时间'步骤,加上额外的列,我没有写在例如),所以我需要以完全自动化(并且希望快速)的方式做到这一点。
我尝试使用join,concat和merge进行全部实现,但是我没有成功。达到预期结果的最佳方法是什么?
答案 0 :(得分:0)
考虑将索引重置为nil
的列,然后仅 @doc false
def changeset(%User{} = user, attrs) do
user
|> cast(attrs, [:email, :phone])
|> validate_required_inclusion([:email, :phone])
|> validate_required_inclusion_format([:email, :phone])
end
defp validate_required_inclusion(changeset, fields) do
if Enum.any?(fields, &present?(changeset, &1)) do
changeset
else
# Add the error to the first field only since Ecto requires a field name for each error.
add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}")
end
end
defp present?(changeset, field) do
value = get_field(changeset, field)
value && value != ""
end
## TODO - this doesnt work
defp validate_required_inclusion_format(changeset, fields) do
if Enum.member?(fields, :email) do
value = get_field(changeset, :email)
if value && value != "" do
IO.inspect(value, label: "email found: ")
changeset
|> email_changeset()
end
end
if Enum.member?(fields, :phone) do
value = get_field(changeset, :phone)
if value && value != "" do
IO.inspect(value, label: "phone found: ")
changeset
|> phone_changeset()
end
end
changeset
end
defp email_changeset(changeset) do
changeset
|> validate_required([:email])
|> validate_format(:email, ~r/.+@.+/)
|> unique_constraint(:email)
end
defp phone_changeset(changeset) do
changeset
|> validate_required([:phone])
|> valid_phone(:phone)
|> unique_constraint(:phone)
end
defp valid_phone(changeset, field) do
phone = get_field(changeset, field)
IO.inspect(phone, label: "phone: ")
{:ok, phone_number} = ExPhoneNumber.parse(phone, "US")
IO.inspect(phone_number, label: "ExPhoneNumber: ")
if ExPhoneNumber.is_valid_number?(phone_number) do
changeset
else
changeset
|> add_error(field, "Invalid Phone Number")
end
end
聚合,以便每组返回一个匹配项并避免重复。然后,运行连接merge
,然后进行列排序并设置多索引。
groupby