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速度快,虽然写法极其优美。