85199


# 困っていること

例のようなデータを入力としてもっています。  
Pythonコードの中で、以下の "実現したい処理" を書きたいが、明らかに冗長な方法しか思いつきません。  
どなたかスマートな書き方を思いつく方、お力を貸してください。

# 入力データ

"小分類ごとの数"と"中分類ごとの上限"が与えられている。

[code]

    中分類 小分類 数量  中分類_上限
    東北  青森  4   10
    東北  岩手  5   10
    東北  宮城  11  10
    関東  東京  8   5
    関東  千葉  3   5
    近畿  大阪  6   3
    近畿  京都  6   3
    九州  福岡  0   2
    九州  佐賀  0   2
    
[/code]

# 実現したい処理

"小分類ごとの数"の合計を"中分類ごとの上限"に収めたい。

[code]

    中分類 小分類 数量  中分類_上限  数量_変更後イメージ
    東北  青森  4   10  2
    東北  岩手  5   10  2
    東北  宮城  11  10  6
    関東  東京  8   5   4
    関東  千葉  3   5   1
    近畿  大阪  6   3   2
    近畿  京都  6   3   1
    九州  福岡  0   2   1
    九州  佐賀  0   2   1
    
[/code]

# 制約事項

・数は必ず0以上の整数にする  
・割合はなるべく変わらないようにする  
・余りは無いようにする


CC BY-SA 4.0
2021-12-17T05:38:52.690
85190
2021-12-17T10:02:34.097
2021-12-17T07:27:45.827
3060
question
python pandas
Python Pandasでのデータ処理を簡潔に書きたい


> 余りは無いようにする

この条件に関しては、かなり適当に調整しています(`idxmax` か `idxmin` に差分を加減)。

[code]

    import pandas as pd
    import numpy as np
    import io
    
    pd.set_option('display.unicode.east_asian_width', True)
    
    data_csv = '''
    中分類,小分類,数量,中分類_上限
    東北,青森,4,10
    東北,岩手,5,10
    東北,宮城,11,10
    関東,東京,8,5
    関東,千葉,3,5
    近畿,大阪,6,3
    近畿,京都,6,3
    九州,福岡,0,2
    九州,佐賀,0,2
    '''.strip()
    
    df = pd.read_csv(io.StringIO(data_csv))
    
    def divide_with_keep_ratio(x):
      n = x['数量'].sum()
      if not n:
        x['数量'] = 1
        n = len(x)
      nums = np.round(x['中分類_上限']*x['数量']/n).astype(int)
      s, limit = nums.sum(), x['中分類_上限'].values[0]
      if s == limit: return nums
      if s > limit:
        nums[nums.idxmax()] -= (s - limit)
      else:
        nums[nums.idxmin()] += (limit - s)
      return nums
    
    df['小分類ごとの数'] = (
      df.groupby('中分類', sort=False)
        .apply(divide_with_keep_ratio).values)
    
    print(df.to_markdown(index=False))
    
[/code]

中分類 | 小分類 | 数量 | 中分類_上限 | 小分類ごとの数  
---|---|---|---|---  
東北 | 青森 | 4 | 10 | 2  
東北 | 岩手 | 5 | 10 | 2  
東北 | 宮城 | 11 | 10 | 6  
関東 | 東京 | 8 | 5 | 4  
関東 | 千葉 | 3 | 5 | 1  
近畿 | 大阪 | 6 | 3 | 1  
近畿 | 京都 | 6 | 3 | 2  
九州 | 福岡 | 0 | 2 | 1  
九州 | 佐賀 | 0 | 2 | 1


CC BY-SA 4.0
2021-12-17T09:24:17.630
85199
2021-12-17T10:02:34.097
2021-12-17T10:02:34.097
47127
47127
85190
answer