各位老铁们好,相信很多人对bitmap 标签都不是特别的了解,因此呢,今天就来为大家分享下关于bitmap 标签以及bitmap标签的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!
本文目录
安卓bitmap可以删除吗BitMap及其在ClickHouse中的应用Vc++:在标签控件中,标签页面是如何添加和创建的bitmap在CDP用户圈包中的应用安卓bitmap可以删除吗此方法返回一个删除了边距的剪切较小的Bitmap.首先将像素传递给int数组,然后使用数组比Bitmap.getPixel方法快一点
只需调用指示源位图和背景颜色的方法.
Bitmapbmp2=removeMargins(bmp,Color.WHITE);
privatestaticBitmapremoveMargins2(Bitmapbmp,intcolor){
//TODOAuto-generatedmethodstub
longdtMili=System.currentTimeMillis();
intMTop=0,MBot=0,MLeft=0,MRight=0;
booleanfound1=false,found2=false;
int[]bmpIn=newint[bmp.getWidth()*bmp.getHeight()];
int[][]bmpInt=newint[bmp.getWidth()][bmp.getHeight()];
bmp.getPixels(bmpIn,0,bmp.getWidth(),0,0,bmp.getWidth(),
bmp.getHeight());
for(intii=0,contX=0,contY=0;ii<bmpIn.length;ii++){
bmpInt[contX][contY]=bmpIn[ii];
contX++;
if(contX>=bmp.getWidth()){
contX=0;
contY++;
if(contY>=bmp.getHeight()){
break;
}
}
}
for(inthP=0;hP<bmpInt[0].length&&!found2;hP++){
//lookingforMTop
for(intwP=0;wP<bmpInt.length&&!found2;wP++){
if(bmpInt[wP][hP]!=color){
Log.e("MTop2","Pixelfound@"+hP);
MTop=hP;
found2=true;
break;
}
}
}
found2=false;
for(inthP=bmpInt[0].length-1;hP>=0&&!found2;hP--){
//lookingforMBot
for(intwP=0;wP<bmpInt.length&&!found2;wP++){
if(bmpInt[wP][hP]!=color){
Log.e("MBot2","Pixelfound@"+hP);
MBot=bmp.getHeight()-hP;
found2=true;
break;
}
}
}
found2=false;
for(intwP=0;wP<bmpInt.length&&!found2;wP++){
//lookingforMLeft
for(inthP=0;hP<bmpInt[0].length&&!found2;hP++){
if(bmpInt[wP][hP]!=color){
Log.e("MLeft2","Pixelfound@"+wP);
MLeft=wP;
found2=true;
break;
}
}
}
found2=false;
for(intwP=bmpInt.length-1;wP>=0&&!found2;wP--){
//lookingforMRight
for(inthP=0;hP<bmpInt[0].length&&!found2;hP++){
if(bmpInt[wP][hP]!=color){
Log.e("MRight2","Pixelfound@"+wP);
MRight=bmp.getWidth()-wP;
found2=true;
break;
}
}
}
found2=false;
intsizeY=bmp.getHeight()-MBot-MTop,sizeX=bmp.getWidth()
-MRight-MLeft;
Bitmapbmp2=Bitmap.createBitmap(bmp,MLeft,MTop,sizeX,sizeY);
dtMili=(System.currentTimeMillis()-dtMili);
Log.e("Margin2",
"Timeneeded"+dtMili+"mSec\nh:"+bmp.getWidth()+"w:"
+bmp.getHeight()+"\narrayx:"+bmpInt.length+"y:"
+bmpInt[0].length);
returnbmp2;
}
标签:android,bitmap
BitMap及其在ClickHouse中的应用问题要从面试或者大数据场景下最常见的一个算法说起,问题是这样的,假如有几十亿个unsignedint类型的数据,要求去重或者计算总共有多少不重复的数据?最简单的办法就是直接利用一个HashMap,进行去重。但是这里面有个内存使用量的问题,几十亿个元素,即使不考虑HashMap本身实现所用到的数据结果,单单key本身,假如每个unsignedint占用4个字节,简单算一下的话,这里都需要几十GB的内存占用,因此,这里就引出了BItMap。
BItMap的思想非常简单,就是用一个bit表示一个二元的状态,比如有或者没有,存在或者不存在,用bit本身的位置信息,对应不同的数据。比如针对上面的问题,我们可以开辟一个2^32bit的内存空间,每一个bit存储一个unsignedint类型的数据,有就是1,没有就是0,总共需要存储unsignedint类型的最大范围个数据,也就是2^32个数据,这个2^32其实就是所谓的基数。如下图所示:
假如存在数字8,那就把对应的第8位的值赋为1。上图插入的数据为1、3、7、8。接着依次把所有的数据遍历然后更新这个BitMap。这样我们就可以得到最终结果。
假如上面的问题变成了对几十亿个URL做判断,那应该怎么去做呢?URL没有办法和BitMap的位置关系对应上,所以,我们需要加一层哈希,把每个URL经过哈希运算得到一个整数,然后对应上BitMap。如下图所示:
但是有哈希,肯定会存在碰撞,如果BitMap基数(也就是长度)比较小,那碰撞的概率就大,如果基数比较大,那占用的空间又会比较多。BloomFilter的思想就是引入多个哈希函数来解决冲突的问题。也就是说对每个URL,经过多个哈希函数的运算,得到多个值,每个数值对应的BitMap的对应的位置都赋值为1。这个两个URL经过多个哈希函数结果还是一样的概率就大大降低。
但是由于依然存在冲突的可能性(其实冲突就是来源于我们BitMap的长度小于了数据量的基数,这也就是牺牲了准确性换来了空间使用的减少),所以BloomFilter存在假阳性的概率,不适用于任何要求100%准确率的场景,也就是说BloomFilter只能用来判无,不能用来判有。比如一个URL经过多次哈希运算之后,发现对应的BitMap的位置都已经是1了,那也不能说明,这个URL之前存在过了,也有可能是哈希冲突的结果。但是一个URL经过多次哈希运算之后,发现对应的BitMap的位置不是都是1,那当前URL之前一定是没有存在过的。
可以看到,BloomFilter引入多次哈希,在查询效率和插入效率不变的情况下,用较少空间的BitMap解决大数据量的判断问题。
大部分情况下仅仅做有无的判断是不能满足使用需求的,我们还是需要真正意义上的BitMap(可以方便的用来做交并等计算),但是最好可以在基数比较大的时候,依然可以占用相对比较小的空间。这就是RoaringBitMap所要实现的。
简单来说RoaringBitMap是BitMap的一种带索引的复杂BitMap数据结构。以32位的RoaringBitMap为例,首先划分2^16个空间(Container),每个Container内部都是一个大小为2^16bit的BitMap,总的内存使用量还是2^32=512Mb。这样的话和普通的BitMap是没有区别的,而RoaringBitMap的创新之处在于每个Container内的BitMap是在没有使用到的情况下是可以不分配内存空间的。这样可以大大减小内存的使用量。
(这个图片是RoaringBitmaps:ImplementationofanOptimizedSoftwareLibrary论文原图)
要将一个4个字节的数据插入RoaringBitMap,首先要用数据的高16位,找到对应的Container,然后用数据的低16在Container中插入。
在每个Container内部,RoaringBitMap不是简单的用BitMap来进行数据的存储,而是把Container的类型划分为几种,不同的Container用来存储不同情况的数据。
当2个字节(4个字节的原数据,低16位用来插入具体的Container中)的数据,总的个数小于4096个的时候,当前Container使用arrayContainer。为什么是4096个呢?4096*2B=8Kb,而一个Container如果是bitmap的结构的话,最多也就是2^16bit=8Kb的空间。所以这里当数据个数小于4096使用arrayContainer会更节省空间。当然这里名字为arrayContainer,实际上是链表结构,不需要最开始就初始化4096个shortint的数组。
当arrayContainer存储的数到4096个的时候(也就是使用内存到8Kb的时候),arrayContainer会转换为bitmapcontainer,bitmapcontainer就是一个2^16bit普通的bitmap,可以存储2^16=65536个数据。这个8Kb还有一个好处,是可以放到L1Cache中,加快计算。
这个严格的说,只是一种数据压缩存储方法的实现。其压缩原理是对于连续的数字只记录初始数字以及连续的长度,比如有一串数字12,13,14,15,16那么经过压缩后便只剩下12,5。从压缩原理我们也可以看出,这种算法对于数据的紧凑程度非常敏感,连续程度越高压缩率也越高。当然也可以实现其他的压缩方法。
RoaringBitMap其核心就在于加了一层索引,利用复杂的数据结构换取了空间上的效率。需要注意的是这里并没有增加计算的复杂度,其出色的数据结构让其在做交并计算的时候性能也毫不逊色。
ClickHouse中有bloom_filter类型的Skippingindexs,可以方便的用来过滤数据。
ClickHouse实现了大量的BitMap的函数,用来操作BitMap。ClickHouse中的BitMap在32位的时候用的是Set实现的,大于32位的时候也是使用RoaringBitMap实现的。我们这里不看具体的函数,我们来看一个典型的使用场景。
最常见的一个场景是根据标签来进行用户的圈选。常见的解决办法是有一张用户标签表,比如
要查询标签tag1='xx'和tag2='xx'的用户需要执行SQL:
但是由于不可能对每个tag列构建一级索引,所以这条SQL执行的效率并不高。可选的一种方式是先构建关于标签的BitMap数据结果,然后进行查询:
(1)创建tag的bitmap表:
(2)写入数据
(3)查询
如果有多张tag表,进行交并计算(要比普通的用户表进行JOIN或者IN计算要高效很多):
Vc++:在标签控件中,标签页面是如何添加和创建的给你段代码参考一下。这段是操作标签控件,以添加标签页。具体点哪个标签显示什么内容,还要在标签的消息响应函数中添加显示/隐藏页面的代码。
OnInitDialog()中进行初始化,代码如下:
CImageListimageList;
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BMP_TABIMG);
imageList.Create(16,16,ILC_COLOR24|ILC_MASK,16,1);
imageList.Add(&bitmap,RGB(255,0,255));
//将控件子类化到控件类变量,就可以象进行类对象操作一样添加标签页
m_tabCtrl.SubclassDlgItem(IDC_TAB_CTRL,this);
m_tabCtrl.SetImageList(&imageList);
TC_ITEMTabCtrlItem;
TabCtrlItem.mask=TCIF_TEXT|TCIF_IMAGE;
TabCtrlItem.iImage=0;
TabCtrlItem.pszText="快捷面板";
m_tabCtrl.InsertItem(0,&TabCtrlItem);
m_pageShortcutBoard.Create(IDD_DLG_SHORTCUT,&m_tabCtrl);
CRectrc;
m_tabCtrl.GetClientRect(&rc);
m_pageShortcutBoard.SetWindowPos(NULL,12,28,rc.right-16,rc.bottom-40,SWP_SHOWWINDOW);
TabCtrlItem.mask=TCIF_TEXT|TCIF_IMAGE;
TabCtrlItem.iImage=1;
TabCtrlItem.pszText="位置记录";
m_tabCtrl.InsertItem(1,&TabCtrlItem);
m_pagePegRecord.Create(IDD_DLG_PEGRECORD,&m_tabCtrl);
m_pagePegRecord.SetWindowPos(NULL,4,28,rc.right-7,rc.bottom-32,SWP_HIDEWINDOW);
imageList.Detach();
bitmap.Detach();
//初始化结束
//选择标签的响应函数,在这里边决定显示哪个页面
voidCPaveShootDlg::OnTcnSelchangeTabCtrl(NMHDR*pNMHDR,LRESULT*pResult)
{
CRectrc;
m_tabCtrl.GetClientRect(&rc);
switch(m_tabCtrl.GetCurSel())
{
case0:
//显示这个页面
m_pageShortcutBoard.SetWindowPos(NULL,12,28,rc.right-16,rc.bottom-40,SWP_SHOWWINDOW);
//隐藏这个页面
m_pagePegRecord.SetWindowPos(NULL,4,28,rc.right-7,rc.bottom-32,SWP_HIDEWINDOW);
break;
case1:
//隐藏这个页面
m_pageShortcutBoard.SetWindowPos(NULL,12,28,rc.right-16,rc.bottom-40,SWP_HIDEWINDOW);
//显示这个页面
m_pagePegRecord.SetWindowPos(NULL,4,28,rc.right-7,rc.bottom-32,SWP_SHOWWINDOW);
break;
}
//*pResult=0;
}
bitmap在CDP用户圈包中的应用用户的标签数据一般存储在多张hive表中,在进行用户圈包,会涉及join的逻辑,限制了人群包数据的产出速度.使用标签进行用户分群,其本质还是集合之间的交、并、补运算。如果能够将符合每个标签取值的用户群提都提前构建出来,即构建好标签-用户的映射关系,在得到人群包的标签组合后直接选取对应的集合,通过集合之间的交/并/补运算即可得到最终的目标人群。bitmap是用于存储标签-用户的映射关系的比较理想的数据结构之一。ClickHouse目前也已经比较稳定的支持了bitmap数据结构,为基于bitmap的用户分群实现提供了基础。
整个方案主要包含以下几个技术问题:
hive提供了基础的row_number()over()函数,但是在操作亿级别行的数据时,会造成数据倾斜,受限于Hadoop集群单机节点的内存限制,无法成功运行。为此提出了一种针对亿级行大数据量的全局唯一连续数字ID生成方法。其核心思想如下:
具体的做法:由于亿级数据不支持全局row_number()排序,可考虑把一张大表按照一定的规则进行分拆,对每个子表打标签,然后分配id,对于第1个子数据集(M=1)的数据,其最终行号是1,2,3,4,…,N1;对于第2个子数据集(M=2)的数据,其最终行号是1+N1,2+N1,3+N1,4+N1,…,N2+N1....以此类推
DMP所有的画像数据最终汇总到了4张Hive表中,分别保存用户的基本信息(base表)、偏好信息(prefer表)、行为信息(action表)和设备信息(device表)。构建好join_id后,还需要将join_id关联到用户画像表中,产出构建bitmap所需要的bitmap_hive表。到此也就完成了Hive数据层的准备工作。
1.标签梳理
2.bitmap的构建和运算转换
具体的见原文档(非公有部分),注意边界值的处理(运算的转化)
这里的处理思想非常有意思https://cloud.tencent.com/developer/news/683175
3.Bitmap_CK表的设计
bitmap数据是通过Spark任务以序列化的方式写入到CH中的,为此我们再CH中创建了一个null引擎的表,bitmap的类型为string。然后以null引擎的表为基础创建了一个物化视图表,通过base64Decode()函数将String类型的bitmap转换成CH中的AggregateFunction(groupBitmap,UInt32)数据结构,最后以物化视图表为物理表,创建分布式表用于数据的查询。同时为了减少CH集群的处理压力,我们还进行了一个优化,即在null引擎表之前创建了一个buffer引擎的表,数据最先写入buffer引擎的表,积攒到一定的时间/批次后,数据会自动写入到null引擎的表。
4.Hive的关系型数据到CH的bitmap数据
Spark任务中,先通过sparkSQL将所需hive数据读取,保存在DataSet<Row>中。根据不同的标签类型设计的规则使用spark聚合算子进行运算。处理逻辑如下:
在这个过程中,我们还使用了bitmap的循环构建、spark任务调优、异常重试机制、bitmap构建后的数据验证等方法来提高任务的运行速度和稳定性。
5.bitmapSQL的生成
通过处理人群包的标签组合,确定所需要的bitmap以及这些bitmap之间的逻辑关系(下图红线标识),最终生成的bitmapSQL示例如下图所示。同时通过使用GLOBALIN代替比较耗时的GLOBALANYINNERJOIN,CHSQL运行效率也有了大幅度的提升。
对于Push消息类的服务需要通过接口获取人群中的数据用于消息发送。由于ClickHouse定位还是OLAP,不适合大量地在线调用,所以需要将人群的数据导入到Mongodb中来提供在线服务调用。为优化分页查询带来的性能问题,在导入Mongodb时为每个版本的每条数据生成一个自增的ID,同时对这个ID建立索引,在查询时根据页数计算出每一页数据的ID范围,然后再根据索引来查询数据,能保证千万级的分页查询平均响应时间在100ms以内。
一些对于bitmap的优化https://cloud.tencent.com/developer/news/680214
RoaringBitmap论文
https://arxiv.org/pdf/1402.6407.pdfhttps://arxiv.org/pdf/1603.06549.pdf
文章到此结束,如果本次分享的bitmap 标签和bitmap标签的问题解决了您的问题,那么我们由衷的感到高兴!