freqCut 1.2
全局最优的等频分组包，通过最小化每组占比与指定占比的残差最小平方和来实现
Requires Python: >=3.0

# freqCut
一个等频分割数列的包，通过最小化每组的实际频数与每组期望频数的残差平方和来寻找一组最优的离散组合分割方法
# 安装
pip install freqCut

# 功能
1.支持左闭右开  
2.最后一组为右闭  
3.通过最小化每组的频数与期望频数的残差平方和来寻找一组最优的分割点。这组分割点是一个全局最优的解析解  
4.因为是全局最优的解析解，所以对集中的数据也给出了一组最优的分割点  
5.用已有的分割点切割另一组数列时，当数列的最大或最小值超出了分割点的最大或最小边界时，可以自动扩展分割点最大和最小值。这使得数列的最大或最小值能够包含在分割的最大组或最小组内    
6.支持带有权重的数列  
News in 0.2.3  
7.支持将指定的特殊值进行单独分组。如果空值没有被指定在特殊值里，则空值单独为一组  
News in 1.1
8.提供了一个对label按数值排序的函数，可供传入apply中进行排序  

# 示例
```
from freqCut.cutter import freq_cut,cut_by_bins,freq_cut_array,sort_label
import numpy as np
import pandas as pd

if __name__=='__main__':
    weight = None
    threshold_distr=0.2
    min_distr = 0.1
    
    #计算分段
    data1 = [4,2,6,np.nan,2,2,None,3,4,10,3,7,1,1,10,5,4,7,7,8,10,8,8,9,10,10]
    bins = freq_cut(data1,threshold_distr,min_distr,weight)
    print('bins:\n',bins,'\n\n')
    
    #按照分段对数量列进行转换
    label_bin1 = cut_by_bins(data1,bins)
    print('label_bin1:\n',label_bin1,'\n\n')
    
    #使用上面的bins，最小值由1变为-1，最大值由10变为11
    data2 = pd.Series([4,2,11,np.nan,-1,2,None,3,4,10,3,7,1,1,11,5,4,7,7,8,-1,2,8,9,10,10])
    #返回的分组label自动扩展了最大和最小值，第一组变为[-1.0,3)，最后一组变为[10,11.0]
    label_bin2 = cut_by_bins(data2,bins)
    #cut_by_bins依据传入数据的类型，可以返回pd.Series,list,tuple（与传入数据类型保持一致）
    print('label_bin2:\n',label_bin2,'\n\n')
    #bins已经被更新，扩展了第一组和最后一组的边界值
    print('updated bins:\n',bins,'\n\n')
       
    
    #将多个数列进行分组。分段按照cutby来计算
    label_bins,bins = freq_cut_array([data1,data2],threshold_distr,min_distr,cutby=0,weight=weight)
    #对比上面的label，可以看到在freq_cut_array中，
    #即使有的数列的最大或最小值没有超出cutby数列所产生的分段，
    #但是由于其他数列的最大值或最小值超出分段所对应的最大或最小值，所以该数列也会被自动扩展。
    #这样是为了组内分段统一（对齐）
    print('label_bins1:\n',label_bins[0],'\n\n')
    print('label_bins2:\n',label_bins[1],'\n\n')
    
    #cutby也可以是一个bins
    bins = freq_cut(data1,threshold_distr,min_distr,weight)
    label_bins,bins = freq_cut_array([data1,data2],threshold_distr,min_distr,cutby=bins,weight=weight)
    print('label_bins1:\n',label_bins[0],'\n\n')
    print('label_bins2:\n',label_bins[1],'\n\n')

    #加入特殊值功能
    data3 = [4,2,6,np.nan,2,2,None,3,4,10,3,7,1,1,10,5,4,7,-999,8,10,-888,8,9,-1000,10]
    spec1 = [{-999,-888},{-1000}]
    spec_bins1 = freq_cut(data3,threshold_distr,min_distr,weight,spec1)
    label_spec1 = cut_by_bins(data3,spec_bins1)
    print('spec_bins1:\n',spec_bins1,'\n\n')
    print('label_spec1:\n',label_spec1,'\n\n')
    
    spec2 = [{-999,-888},{-1000,None}]
    label_spec2,spec_bins2 = freq_cut_array([data2,data3],threshold_distr,min_distr,cutby=1,weight=weight,spec_value=spec2)    
    print('spec_bins2:\n',spec_bins2,'\n\n')
    print('label_spec2:\n',label_spec2[0],'\n\n')
    print('label_spec3:\n',label_spec2[1],'\n\n')
    
    #加入一个对label的排序函数，方便使用
    data = pd.Series([4,2,6,np.nan,2,2,None,3,4,10,3,7,1,1,10,5,4,7,-999,8,10,-888,8,11,-1000,10])
    spec = [{-999,-888},{-1000}]
    spec_bins = freq_cut(data,threshold_distr,min_distr,weight,spec)
    label_spec = cut_by_bins(data,spec_bins)
    #sort是按照字符排序的
    print('label_spec_dis:\n',label_spec.value_counts().sort_index(),'\n\n')
    #提供了一个对label按照数值排序的函数
    dis= label_spec.value_counts()
    dis = dis[sorted(dis.index,key=sort_label)]
    print('label_spec_dis:\n',dis,'\n\n')


```

# API
## value_counts_weight(data,weight=None)  
类似pandas中的value_counts，但可以支持权重  
data:需要被等频分组的数列.array_like  
weight:data中每个数据的权重，如果权重相等则为None，默认为None。array_like  
return  
data每个取值的数量。pd.Series  
data每个取值的占比。pd.Series  

## freq_cut(data,threshold_distr,min_distr,weight=None,spec_value=None)  
等频分割，给出一组全局最优的分割点
data:需要被等频分组的数列.array_like  
threshold_distr:当小于1时，每个组期望的占比。当大于1时，分成多少组  
min_distr:由于数据可能偏斜或集中，所以无法严格遵守threshold_distr，此时你所能接受的最小的分组占比  
weight:与data对应的权重。array_like  
spec_value:指定的特殊值的分组。例如：({-9999,-9998},{-8888,None},{-1000})。如果None没有出现在任何一个特殊值组中，则空值单成一组  
return  
bins:分组信息。例如 ['[1,3)', '[3,5)', '[5,8)', '[8,10)', '[10,10]']。有特殊值的示例：['[-1.0,3.0)', '[3.0,6.0)', '[6.0,9.0)', '[9.0,11.0]', '{-888, -999}', '{-1000, None}'。 list  

## cut_by_bins(data,bins)  
按照指定的bins来对data进行分割，如果data中去除特殊值后的最大或最小值超出bins的最大或最小的边界值，则自动扩展bins的边界值，使得超出的数值能够被划分进最大或最小的一组  
data:需要被分组的数列.array_like  
bins:分组信息。例如 ['[1,3)', '[3,5)', '[5,8)', '[8,10)', '[10,10]']。list  
return  
data对应的label。返回类型与data保持一致  

## freq_cut_array(datas,threshold_distr,min_distr,cutby=0,weight=None,spec_value=None)
将一组数列进行统一的分割，负责计算分割点的数列由cutby来指定。如果通过cutby数列得到bins后，让datas中的每个数列单独调用cut_by_bins，由于bins会被自动扩展边界值，所以不能保持datas中数列的分组label的一致性（不一致来自最大组和最小组）。freq_cut_array能够让datas中的所有分组保持一致  
datas:需要被等频分组的多个数列。  
threshold_distr:当小于1时，每个组期望的占比。当大于1时，分成多少组  
min_distr:由于数据可能偏斜或集中，所以无法严格遵守threshold_distr，此时你所能接受的最小的分组占比  
cutby:datas中被用来计算分段的数列的序号。也可以是一个直接是bins。  
weight:与data对应的权重。array_like   
spec_value：指定的特殊值的分组。例如：({-9999,-9998},{-8888,None},{-1000})。如果None没有出现在任何一个特殊值组中，则空值单成一组  
return  
datas中的每个数列对应的label，label的类型同datas[i]  
bins