![](/img/trans.png)
[英]Pythonic way to find key of weighted minimum and maximum from a dictionary
[英]How to apply panda group by with minimum and maximum size condition (Pythonic way)
我在 pandas 中有一个 dataframe ,我需要将其分组并存储在一个新数组中,在该数组中我需要具有特定大小的每个组的大小,如果超过最小大小,则应将其添加到具有最小的尺寸。 例如,在我对数据进行分组后,我将拥有一组G
,即len(G)<=b
、 len(G)>=a
或a <= len(G) <= b
。 所以,我需要用len(G)>=a
使组满足条件a <= len(G) <= b
。
该代码现在正在运行。 所以,我想知道是否有更方便的方法来做到这一点。
import numpy as np
import pandas as pd
rng = np.random.default_rng() # Just for testing
df = pd.DataFrame(rng.integers(0, 10, size=(1000, 4)), columns=list('ABCD'))
# The dataframe is grouped depend on specific column.
ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3], as_index=False)]
n = 20 # The maximum size of the group is 25
new_arrayi_index = 0
new_array = []
for count_index in range(len(ans)):
l = ans[count_index]
if len(l) > n:
df_shuffled = pd.DataFrame(l).sample(frac=1)
final = [df_shuffled[i:i+n] for i in range(0,df_shuffled.shape[0],n)]
for inde in range(len(final)):
if len(final[inde]) <= 5 and new_arrayi_index != 0: #The minimum size of the group is 5
new_array[new_arrayi_index - 1]=new_array[new_arrayi_index - 1]+final[inde]
else:
new_array.append(final[inde])
new_arrayi_index += 1
else:
new_array.append(l)
new_arrayi_index += 1
count_index_ = 0
for count_index in range(len(new_array)):
print("count", count_index, "Size", len(new_array[count_index]))
print(new_array[count_index])
count_index_ += count_index
print(count_index_)
将此行 -> ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3], as_index=False)]
更改为ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3].min(), as_index=False)]
for min
和ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3].max(), as_index=False)]
for max
我写了一个 function 将 dataframe 分成等于最大大小的块。 它检查最后一个块的剩余部分的大小,如果剩余部分小于最小大小,它将最后两个块分成大小大致相等的两个块。
在拆分大型 pandas dataframe时建立答案
import numpy as np
import pandas as pd
rng = np.random.default_rng(seed=1) # Just for testing
df = pd.DataFrame(rng.integers(0, 10, size=(1000, 4)), columns=list('ABCD'))
# The dataframe is grouped depend on specific column.
n = 20 # The maximum size of the group is 25
# https://stackoverflow.com/questions/17315737/split-a-large-pandas-dataframe
def split_dataframe(df, chunk_size=20, min_size=10):
chunks = list()
remainder = len(df) % chunk_size
if 0 < remainder < min_size:
num_chunks = len(df) // chunk_size - 1
for i in range(num_chunks):
chunks.append(df[i * chunk_size:(i + 1) * chunk_size])
df_ = df[(num_chunks) * chunk_size:]
last_break = int(len(df_) / 2)
chunks.append(df_[:last_break])
chunks.append(df_[last_break:])
return chunks
else:
num_chunks = len(df) // chunk_size + 1
for i in range(num_chunks):
chunks.append(df[i*chunk_size:(i+1)*chunk_size])
return chunks
new_array = []
for group, df_ in df.groupby(df.columns[3], as_index=False):
new_array.extend(split_dataframe(df_))
count_index_ = 0
for count_index in range(len(new_array)):
print("count", count_index, "Size", len(new_array[count_index]))
print(new_array[count_index])
count_index_ += count_index
print(count_index_)
我从一开始就关注这篇文章,对讨论将如何进行感到好奇,因为 OP 的问题并不总是可以解决。
举个例子:一个组有 19 个元素,你想把它分成大小在 10 到 15 之间的部分。
当且仅当存在 integer g 时,解决方案才存在,使得n/b <= g <= n/a
。 在这种情况下,您可以看到长度a
g
部分将使用g*a <= n
元素,长度为b
的部分将使用g*b >= n
。
在这种情况下,也可以有一个平衡分区,最大的部分最多比最小的部分大一个记录(最小的部分将有n//g
条记录)。
我们可以对问题进行轻微修改,将其拆分为尽可能少的部分,每个部分最多包含b
条记录。 使得每个部分的长度满足a <= len(s) <= a+1
。
请注意,在这种情况下,我们将a
与b
最接近,以便问题有解决方案。 对于可解决的问题,解决方案将是原始问题的解决方案,对于无法解决的问题,它将通过减少a
来修改原始需求,以便问题可以解决。
上面的示例将变为: 在不超过 15 个元素的尽可能少的平衡组中拆分 19 个元素。 然后解决方案是包含 10 个元素的部分和 9 个元素的部分。
def group_and_split(df, b, column):
'''
- df : a datafame
- b : the largest allowed section
- column: the column by which the data must be grouped
'''
# doing it in a pythonic way
return [np.array_split(y, (len(y)+b-1)//b)
for x, y in df.groupby(column, as_index=False)]
您可以检查它是否为重述的问题提供了解决方案
pd.DataFrame([{
'num-sections': len(g),
'largest-section': max(len(gi) for gi in g),
'smallest-sections':min(len(gi) for gi in g)
} for g in group_and_split(df, 25, 'D')])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.