Pandas:追加行的副本,仅更改多列中大于允许拆分 bin 值的最大值的值
Pandas: Append copy of rows changing only values in multiple columns larger than max allowed to split bin values
问题: 我有一个数据框需要根据特定列的值进行修改。如果任何列值的值大于允许的最大值,则将根据分配到大小相等的 bin 中创建一个新行(在数据值和最大允许值之间进行整数除法)
Table 和解释:
原文:
Index
Data 1
Data 2
Max. Allowed
1
1
2
3
2
10
5
8
3
7
12
5
必填:
Index 括号中的值指的是原始索引值
Index
Data 1
Data 2
Max. Allowed
1 (1)
1
2
3
2 (2)
8
5
8
3
2
0
8
4 (3)
5
5
5
5
2
5
5
6
0
2
5
由于原始索引 = 2,Data1 = 10 大于允许的最大值 = 8。此行已分为两行,如上所示 table。
尝试: 我能够找到那些值大于允许的最大值和要插入的行数的列。但是我很困惑,如果两列的值都大于最大允许值(如索引 = 3 的情况),那么这种方法是否可行。这些值指示要为特定列的每个索引值插入多少行。
Index
Data 1
Data 2
1
0
0
2
1
0
3
1
2
让我们按照以下步骤进行处理:
第一步:准备分割值:
定义自定义 lambda 函数,将 Data 1
、Data 2
转换为值列表,如果大于 Max. Allowed
,则用 Max. Allowed
拆分。将扩展列表保存在 2 个新列中 Data 1x
、Data 2x
:
f = lambda x, y, z: [z] * (x // z) + [x % z] + [0] * (max(x//z, y//z) - x//z)
df['Data 1x'] = df.apply(lambda x: f(x['Data 1'], x['Data 2'], x['Max. Allowed']) , axis=1)
df['Data 2x'] = df.apply(lambda x: f(x['Data 2'], x['Data 1'], x['Max. Allowed']) , axis=1)
lambda函数的设计目的是在列表中加0,使同一行列表中的元素个数长度相同
中间结果:
print(df)
Index Data 1 Data 2 Max. Allowed Data 1x Data 2x
0 1 1 2 3 [1] [2]
1 2 10 5 8 [8, 2] [5, 0]
2 3 7 12 5 [5, 2, 0] [5, 5, 2]
第 2 步:将拆分值分解成单独的行:
情况一:如果你的Pandas版本是1.3或以上
我们使用DataFrame.explode()
展开2个新列:(这部分展开多个列的功能需要Pandas1.3或以上版本)
df = df.explode(['Data 1x', 'Data 2x'])
案例2: Pandas 1.3以下的版本,尝试以下方式爆破:
df = df.apply(pd.Series.explode)
案例3:如果以上2种爆破方式在你的编程环境中不起作用,请使用:
df_exp = df.explode('Data 1x')[['Index', 'Data 1', 'Data 2', 'Max. Allowed']].reset_index(drop=True)
df_1x = df.explode('Data 1x')[['Data 1x']].reset_index(drop=True)
df_2x = df.explode('Data 2x')[['Data 2x']].reset_index(drop=True)
df = df_exp.join([df_1x, df_2x])
结果:
print(df)
Index Data 1 Data 2 Max. Allowed Data 1x Data 2x
0 1 1 2 3 1 2
1 2 10 5 8 8 5
1 2 10 5 8 2 0
2 3 7 12 5 5 5
2 3 7 12 5 2 5
2 3 7 12 5 0 2
第 3 步:格式化为所需的输出:
# select and rename columns
df = (df[['Index', 'Data 1x', 'Data 2x', 'Max. Allowed']]
.rename({'Data 1x': 'Data 1', 'Data 2x': 'Data 2'}, axis=1)
.reset_index(drop=True)
)
# reset the `Index` values
df['Index'] = df.index + 1
最终结果:
print(df)
Index Data 1 Data 2 Max. Allowed
0 1 1 2 3
1 2 8 5 8
2 3 2 0 8
3 4 5 5 5
4 5 2 5 5
5 6 0 2 5
假设您愿意逐行处理数据框,那么您可以在 while 循环中检查最大值并用新行填充新数据框。
import pandas as pd
df = pd.DataFrame({"Index" : [1, 2, 3], "Data 1" : [1,10,7], "Data 2" : [2,5,12], "Max_Allowed" : [3,8,5]})
print(df)
# create a new data frame that we can populate with rows of data
dfz = pd.DataFrame(columns=("Index", "Data 1","Data 2","Max_Allowed"))
iz = 0
for(_, col1, col2, col3, col4) in df.itertuples(name=None):
if col2<=col4 and col3<=col4:
dfz.loc[iz] = [str(iz+1)+"("+str(col1)+")", col2, col3, col4]
iz += 1
else:
iz_orig = iz # keep the index we are at currently
while col2>0 or col3>0:
if col2>col4: # check if more than maximum value for Data 1
col2p=col4
col2 -= col4 # minus the maximum value from current value
else:
col2p=col2
col2 = 0 # set the value to zero
if col3>col4: # check if more than maximum value for Data 2
col3p=col4
col3 -= col4
else:
col3p=col3
col3 = 0
if iz_orig == iz:
# enter with the original Index in parenthesis
dfz.loc[iz] = [str(iz+1)+"("+str(col1)+")", col2p, col3p, col4]
else:
# enter row with just the new Index
dfz.loc[iz] = [str(iz+1), col2p, col3p, col4]
iz += 1
print(dfz)
问题: 我有一个数据框需要根据特定列的值进行修改。如果任何列值的值大于允许的最大值,则将根据分配到大小相等的 bin 中创建一个新行(在数据值和最大允许值之间进行整数除法)
Table 和解释:
原文:
Index | Data 1 | Data 2 | Max. Allowed |
---|---|---|---|
1 | 1 | 2 | 3 |
2 | 10 | 5 | 8 |
3 | 7 | 12 | 5 |
必填:
Index 括号中的值指的是原始索引值
Index | Data 1 | Data 2 | Max. Allowed |
---|---|---|---|
1 (1) | 1 | 2 | 3 |
2 (2) | 8 | 5 | 8 |
3 | 2 | 0 | 8 |
4 (3) | 5 | 5 | 5 |
5 | 2 | 5 | 5 |
6 | 0 | 2 | 5 |
由于原始索引 = 2,Data1 = 10 大于允许的最大值 = 8。此行已分为两行,如上所示 table。
尝试: 我能够找到那些值大于允许的最大值和要插入的行数的列。但是我很困惑,如果两列的值都大于最大允许值(如索引 = 3 的情况),那么这种方法是否可行。这些值指示要为特定列的每个索引值插入多少行。
Index | Data 1 | Data 2 |
---|---|---|
1 | 0 | 0 |
2 | 1 | 0 |
3 | 1 | 2 |
让我们按照以下步骤进行处理:
第一步:准备分割值:
定义自定义 lambda 函数,将 Data 1
、Data 2
转换为值列表,如果大于 Max. Allowed
,则用 Max. Allowed
拆分。将扩展列表保存在 2 个新列中 Data 1x
、Data 2x
:
f = lambda x, y, z: [z] * (x // z) + [x % z] + [0] * (max(x//z, y//z) - x//z)
df['Data 1x'] = df.apply(lambda x: f(x['Data 1'], x['Data 2'], x['Max. Allowed']) , axis=1)
df['Data 2x'] = df.apply(lambda x: f(x['Data 2'], x['Data 1'], x['Max. Allowed']) , axis=1)
lambda函数的设计目的是在列表中加0,使同一行列表中的元素个数长度相同
中间结果:
print(df)
Index Data 1 Data 2 Max. Allowed Data 1x Data 2x
0 1 1 2 3 [1] [2]
1 2 10 5 8 [8, 2] [5, 0]
2 3 7 12 5 [5, 2, 0] [5, 5, 2]
第 2 步:将拆分值分解成单独的行:
情况一:如果你的Pandas版本是1.3或以上
我们使用DataFrame.explode()
展开2个新列:(这部分展开多个列的功能需要Pandas1.3或以上版本)
df = df.explode(['Data 1x', 'Data 2x'])
案例2: Pandas 1.3以下的版本,尝试以下方式爆破:
df = df.apply(pd.Series.explode)
案例3:如果以上2种爆破方式在你的编程环境中不起作用,请使用:
df_exp = df.explode('Data 1x')[['Index', 'Data 1', 'Data 2', 'Max. Allowed']].reset_index(drop=True)
df_1x = df.explode('Data 1x')[['Data 1x']].reset_index(drop=True)
df_2x = df.explode('Data 2x')[['Data 2x']].reset_index(drop=True)
df = df_exp.join([df_1x, df_2x])
结果:
print(df)
Index Data 1 Data 2 Max. Allowed Data 1x Data 2x
0 1 1 2 3 1 2
1 2 10 5 8 8 5
1 2 10 5 8 2 0
2 3 7 12 5 5 5
2 3 7 12 5 2 5
2 3 7 12 5 0 2
第 3 步:格式化为所需的输出:
# select and rename columns
df = (df[['Index', 'Data 1x', 'Data 2x', 'Max. Allowed']]
.rename({'Data 1x': 'Data 1', 'Data 2x': 'Data 2'}, axis=1)
.reset_index(drop=True)
)
# reset the `Index` values
df['Index'] = df.index + 1
最终结果:
print(df)
Index Data 1 Data 2 Max. Allowed
0 1 1 2 3
1 2 8 5 8
2 3 2 0 8
3 4 5 5 5
4 5 2 5 5
5 6 0 2 5
假设您愿意逐行处理数据框,那么您可以在 while 循环中检查最大值并用新行填充新数据框。
import pandas as pd
df = pd.DataFrame({"Index" : [1, 2, 3], "Data 1" : [1,10,7], "Data 2" : [2,5,12], "Max_Allowed" : [3,8,5]})
print(df)
# create a new data frame that we can populate with rows of data
dfz = pd.DataFrame(columns=("Index", "Data 1","Data 2","Max_Allowed"))
iz = 0
for(_, col1, col2, col3, col4) in df.itertuples(name=None):
if col2<=col4 and col3<=col4:
dfz.loc[iz] = [str(iz+1)+"("+str(col1)+")", col2, col3, col4]
iz += 1
else:
iz_orig = iz # keep the index we are at currently
while col2>0 or col3>0:
if col2>col4: # check if more than maximum value for Data 1
col2p=col4
col2 -= col4 # minus the maximum value from current value
else:
col2p=col2
col2 = 0 # set the value to zero
if col3>col4: # check if more than maximum value for Data 2
col3p=col4
col3 -= col4
else:
col3p=col3
col3 = 0
if iz_orig == iz:
# enter with the original Index in parenthesis
dfz.loc[iz] = [str(iz+1)+"("+str(col1)+")", col2p, col3p, col4]
else:
# enter row with just the new Index
dfz.loc[iz] = [str(iz+1), col2p, col3p, col4]
iz += 1
print(dfz)