public class NumericStockData { /// <summary> /// 取值 /// </summary> public double? Value { get; set; } public NumericStockData() { } public NumericStockData(DateTime time,string stockCode,double? value) { Time = time; StockCode = stockCode; Value = value; } } public partial class NumericStockDataList:List<NumericStockData> { public NumericStockDataList() : base() { } public NumericStockDataList(IEnumerable<NumericStockData> linq) : base(linq) { } public NumericStockDataList(int capacity) : base(capacity) { } }
操作一:对一个集合StockList按照时间分组后,组内求均值,将此均值覆盖组内的每一项。返回每一项。
示例图:
第一步,按照时间点分组
日期 | 股票代码 | 收盘价 |
2014/01/25 | 000001 | 14.5 |
2014/01/25 | 000002 | 21.7 |
2014/01/26 | 000001 | 14.9 |
2014/01/26 | 000002 | 22.5 |
2014/01/27 | 000001 | 15.3 |
2014/01/27 | 000002 | 21.4 |
第二步:分组计算
日期 | 股票代码 | 收盘价 |
2014/01/25 | 000001 | 18.1 |
2014/01/25 | 000002 | 18.1 |
2014/01/26 | 000001 | 18.7 |
2014/01/26 | 000002 | 18.7 |
2014/01/27 | 000001 | 18.35 |
2014/01/27 | 000002 | 18.35 |
public static NumericStockDataList Gave1(NumericStockDataList StockList)
{
IEnumerable<NumericStockData> linq =
from stock in StockList
group stock by stock.Time into g//按时间分组
let avg = g.Average(y => y.Value)//组均值
from gstock in g
select new NumericStockData()
{
Time = gstock.Time,
StockCode = gstock.StockCode,
Value = avg
};
return new NumericStockDataList(linq);
}
操作二:
1. 按照日期进行分组
2. 分组按照排序要求 DESC对指标值进行排序
3. 分组填写排序结果,增加一列排名列。分组根据顺序填写排名顺序。
示例图:
RANK(现价, DESC)
① 数据 |
| ||||||
股票代码 | 现价 |
| |||||
6501 | 100 |
| |||||
6502 | 200 |
| |||||
6503 | 300 |
| |||||
6504 | 400 |
| |||||
6505 | 500 |
| |||||
6506 | 700 |
| |||||
6507 | 650 |
| |||||
6508 | 300 |
| |||||
| ② 降序排序 |
| |||||
| 股票代码 | 现价 |
| ||||
| 6506 | 700 |
| ||||
| 6507 | 650 |
| ||||
| 6505 | 500 |
| ||||
| 6504 | 400 |
| ||||
| 6503 | 300 |
| ||||
| 6508 | 300 |
| ||||
| 6502 | 200 |
| ||||
| 6501 | 100 |
| ||||
| ③ 进行排名 |
| |||||
| 股票代码 | 现价 | 排名 | ||||
| 6506 | 700 | 1 | ||||
| 6507 | 650 | 2 | ||||
| 6505 | 500 | 3 | ||||
| 6504 | 400 | 4 | ||||
| 6503 | 300 | 5 | ||||
| 6508 | 300 | 5 | ||||
| 6502 | 200 | 7 | ||||
| 6501 | 100 | 8 | ||||
④ 结果 |
| |
股票代码 | 现价 | 结果 |
6501 | 100 | 8 |
6502 | 200 | 7 |
6503 | 300 | 5 |
6504 | 400 | 4 |
6505 | 500 | 3 |
6506 | 700 | 1 |
6507 | 650 | 2 |
6508 | 300 | 5 |
实现方法:
操作三:public static NumericStockDataList Rank(NumericStockDataList StockList) { linq = from stock in StockList group stock by stock.Time into g//按时间分组 from gInside in g orderby gInside.Value ascending, gInside.Value.HasValue descending let gList = g.OrderByDescending(y => y.Value).ToList()//组内按值降序 select new NumericStockData() { Time = gInside.Time, StockCode = gInside.StockCode, Value = (double?)gList.FindIndex(x => x.Value == gInside.Value) + 1,//按所在位置排名,若有重复取第一次出现的位置 }; return new NumericStockDataList(linq); }
1. 对成分股按照日期进行分组
2. 系统对股票指标值进行降序排序,
3. 新增一列,根据排序顺序填写排名
4. 对排名结果进行调整,具体规则为:指标值相同的,排名结果为相同指标值的排序做数值平均运算
5. 计算出各组排名的上下限数值
6. 将成分股排名数值与分组的排名数值上下限进行比较,对股票进行分组
示例图:
QUANTILE(现价,NUMSTOCKS,5)
<根据股票个数分组>
1.数据 2.用现价降序排序,再排名
股票代码 | 现价 | | 现价 | 排名1 | 排名调整 |
6501 | 100 |
| 5000 | 1 | 1 |
6502 | 200 |
| 4500 | 2 | 2 |
6503 | 300 |
| 4200 | 3 | 3 |
6504 | 400 |
| 4000 | 4 | 4 |
6505 | 500 |
| 2000 | 5 | 5 |
6506 | 700 |
| 1000 | 6 | 6 |
6507 | 650 |
| 700 | 7 | 7.5 |
6508 | 300 |
| 700 | 8 | 7.5 |
6509 | 300 |
| 650 | 9 | 9.5 |
6510 | 400 |
| 650 | 10 | 9.5 |
6511 | 500 |
| 600 | 11 | 11.5 |
6512 | 700 |
| 600 | 12 | 11.5 |
6513 | 650 |
| 500 | 13 | 14 |
6514 | 300 | 500 | 14 | 14 | |
6515 | 1000 |
| 500 | 15 | 14 |
6516 | 2000 |
| 400 | 16 | 17 |
6517 | 300 |
| 400 | 17 | 17 |
6518 | 4200 |
| 400 | 18 | 17 |
6519 | 4500 |
| 300 | 19 | 22 |
6520 | 600 |
| 300 | 20 | 22 |
6521 | 400 |
| 300 | 21 | 22 |
6522 | 5000 |
| 300 | 22 | 22 |
6523 | 4000 |
| 300 | 23 | 22 |
6524 | 600 |
| 300 | 24 | 22 |
6525 | 500 |
| 300 | 25 | 22 |
6526 | 300 |
| 200 | 26 | 26 |
6527 | 300 |
| 100 | 27 | 27 |
3.分组
每个组合的股票个数=股票数/分组数
每个组合的股票个数=27/5=5.4
| 下边线 | 上边线 | 排名 |
第1组 | 0 | 5.4 | 1 |
第2组 | 5.4 | 10.8 | 2 |
第3组 | 10.8 | 16.2 | 3 |
第4组 | 16.2 | 21.6 | 4 |
第5组 | 21.6 | 27 | 5 |
4.分位化
股票代码 | 现价 | 排名调整 | 结果 |
6522 | 5000 | 1 | 1 |
6519 | 4500 | 2 | 1 |
6518 | 4200 | 3 | 1 |
6523 | 4000 | 4 | 1 |
6516 | 2000 | 5 | 1 |
6515 | 1000 | 6 | 2 |
6506 | 700 | 7.5 | 2 |
6512 | 700 | 7.5 | 2 |
6507 | 650 | 9.5 | 2 |
6513 | 650 | 9.5 | 2 |
6520 | 600 | 11.5 | 3 |
6524 | 600 | 11.5 | 3 |
6505 | 500 | 14 | 3 |
6511 | 500 | 14 | 3 |
6525 | 500 | 14 | 3 |
6504 | 400 | 17 | 4 |
6510 | 400 | 17 | 4 |
6521 | 400 | 17 | 4 |
6503 | 300 | 22 | 5 |
6508 | 300 | 22 | 5 |
6509 | 300 | 22 | 5 |
6514 | 300 | 22 | 5 |
6517 | 300 | 22 | 5 |
6526 | 300 | 22 | 5 |
6527 | 300 | 22 | 5 |
6502 | 200 | 26 | 5 |
6501 | 100 | 27 | 5 |
实现方法:
public static NumericStockDataList Quantile(NumericStockDataList StockList, string type, double nValue) { IEnumerable<NumericStockData> linq = from rankedList in ( from stock in StockList.AddNullToBeMatrix() group stock by stock.Time into g//按时间分组 let gList = g.OrderByDescending(x => x.Value).ToList()//现价降序 let HasNull = g.Where(x => !x.Value.HasValue).Count() > 0 from stockGroup in g let firstIndex = HasNull ? null : (double?)gList.FindIndex(x => x.Value == stockGroup.Value) + 1//相同值第一次出现的下标 let lastIndex = HasNull ? null : (double?)gList.FindLastIndex((int)firstIndex - 1, x => x.Value == stockGroup.Value) + 1//相同值最后一次出现的下标 let seperate = (double)g.Count() / n //排名:组内自然排序后,相同的值取排序值均值 select new { Time = stockGroup.Time, StockCode = stockGroup.StockCode, Value = firstIndex == null ? null : (double?)(firstIndex + lastIndex) / 2,//排名:当Value出现空值时,排名为空 Seperate = seperate, IndustryIds = stockGroup.IndustryIds } ) orderby rankedList.Value//按排名升序 select new NumericStockData() { Time = rankedList.Time, StockCode = rankedList.StockCode, Value = rankedList.Value.HasValue ? System.Math.Ceiling((double)rankedList.Value / rankedList.Seperate) : rankedList.Value,//组号=排名/组数,向上取整;当排名为空时,组号为空 IndustryIds = rankedList.IndustryIds }; return new NumericStockDataList(linq); }
总结:
1。这样理解分组的linq:分组后,每一组都是IEnumerable类型,可以对组内的项迭代。
迭代的方法即from x in xx。即可看作foreach(var x in xx)
2.巧用判断条件:
let:声明局部变量,其作用范围是嵌套在每个from x in xx之间。可以在此处设置判断条件
即:from x in xx
let v=1;
group x by x.Time into g
from gInside in g
let y=2
select {};
等价于 将xx分组后
var v=1;
foreach(var g in xx.GroupBy(i=>i.Tme))
{
foreach(var gInside in g)
{
var y=2;
}
3。注意:对于大数据量(100000数量级以上),使用linq分组、筛选、排序的组合组度很慢。远不如for速度快,虽然写法极其优美。