xulihang's blog 2026-08-03T09:51:22+00:00 xulihanghai@163.com 学习Power BI 2026-08-03T07:39:50+00:00 xulihang blog.xulihang.me/learn-about-power-bi Power BI是微软推出的一套强大的商业分析工具。它最早可追溯到Excel中的Power Pivot,Power Query。它在2015年正式发布,包含网页端的Power BI Service、桌面端的Power BI Desktop和移动版本。

Power BI专注于数据导入、清洗、呈现这一整个流程,和Excel相比,更适合可视化和分析数据。

比如我有个在线的demo,将每天统计的数据以csv的格式,保存为一个个文件,命名为YYYYMMDD.txt,例如下方示例:

2026/07/14 18:53:00, IP, Country, UserAgent, OCR
2026/07/14 18:53:00, IP, Country, UserAgent, Save

原始数据包含事件时间,访问者IP,访问者国家,浏览器UserAgent和事件名字,比如OCR、保存等等。

可以在Power BI中先建一个Semantic Model,通过Web获取这些数据,然后解析并保存。可以使用高级编辑器,使用以下命令进行存储:

let
    // 1. 定义基础域名和相对路径
    // 使用 RelativePath 参数实现相对路径引用
    BaseUrl = "https://blog.xulihang.me",
    
    // 2. 定义获取数据的日期范围(最近30天)
    Days = 30,
    EndDate = Date.AddDays(Date.From(DateTime.LocalNow()), -1),
    StartDate = Date.AddDays(EndDate, -(Days - 1)),
    
    // 3. 生成日期列表
    DateList = List.Dates(StartDate, Days, #duration(1,0,0,0)),
    
    // 4. 定义读取单个日志文件的函数
    GetLog = (LogDate as date) as nullable table =>
        let
            // 将日期转为 YYYYMMDD 格式
            DateStr = Date.ToText(LogDate, "yyyyMMdd"),
            
            // 使用 Web.Contents 和 RelativePath 获取文件
            Response = try Web.Contents(
                BaseUrl,
                [
                    RelativePath = "/" & DateStr & ".txt"
                ]
            ),
            
            // 如果文件不存在则返回 null
            Result = if Response[HasError] then
                null
            else
                let
                    // 读取文件内容并按行分割
                    Lines = Lines.FromBinary(Response[Value], null, null, 65001),
                    
                    // 解析每一行 CSV
                    ParseLine = (line as text) =>
                        let
                            Parts = Text.Split(line, ","),
                            Count = List.Count(Parts),
                            
                            // 假设至少有5列:时间, IP, 国家, UserAgent, 事件名称
                            // 如果列数多于5,将中间多余列合并(处理 UserAgent 中可能包含逗号的情况)
                            Result = if Count >= 5 then
                                {
                                    Parts{0},  // 事件时间
                                    Parts{1},  // IP
                                    Parts{2},  // 国家
                                    Text.Combine(List.Range(Parts, 3, Count - 4), ","),  // UserAgent(可能包含逗号)
                                    Parts{Count-1}  // 事件名称(最后一列)
                                }
                            else
                                null
                        in
                            Result,
                    
                    // 过滤掉空行和解析失败的行
                    Parsed = List.RemoveNulls(List.Transform(Lines, each ParseLine(_))),
                    
                    // 转换为表格
                    TableData = Table.FromRows(
                        Parsed,
                        {"事件时间", "IP", "国家", "UserAgent", "事件名称"}
                    ),
                    
                    // 添加日志日期列
                    AddDate = Table.AddColumn(
                        TableData,
                        "日志日期",
                        each LogDate,
                        type date
                    )
                in
                    AddDate
        in
            Result,
    
    // 5. 遍历所有日期,获取数据
    Tables = List.RemoveNulls(List.Transform(DateList, each GetLog(_))),
    
    // 6. 统一所有表的结构(确保列一致)
    CleanTables = List.Transform(
        Tables,
        each Table.SelectColumns(
            _,
            {"事件时间", "IP", "国家", "UserAgent", "事件名称", "日志日期"},
            MissingField.UseNull
        )
    ),
    
    // 7. 合并所有表
    Combined = Table.Combine(CleanTables),
    
    // 8. 设置正确的数据类型
    ChangedType = Table.TransformColumnTypes(
        Combined,
        {
            {"事件时间", type datetime},
            {"IP", type text},
            {"国家", type text},
            {"UserAgent", type text},
            {"事件名称", type text},
            {"日志日期", type date}
        }
    )
in
    ChangedType

以上存储的是原始数据,我们如果想基于原始数据计算出更多列,可以新建一个列,比如根据UserAgent计算操作系统:

OS = 
VAR UA = LOWER(RawLogs[UserAgent])
RETURN
SWITCH(
    TRUE(),
    CONTAINSSTRING(UA,"windows"),"Windows",
    CONTAINSSTRING(UA,"android"),"Android",
    CONTAINSSTRING(UA,"iphone") || CONTAINSSTRING(UA,"ipad"),"iOS",
    CONTAINSSTRING(UA,"macintosh"),"macOS",
    CONTAINSSTRING(UA,"linux"),"Linux",
    "Other"
)

一个经常用到的概念叫Measure,它不是存储在表格中的数据,而是需要用到时,会动态计算的一个指标。比如以下计算唯一IP数量的指标。在可视化报表中,Measure可以根据各种条件,比如时间进行动态计算。

Visitors = 
DISTINCTCOUNT(RawLogs[IP])

有了Semantic Model后,我们可以基于这个数据创建可视化报表了。有常见的饼图、折线图、漏斗图等等。

相似产品

  • Apache Superset
  • 帆软FineBI
  • Tableau
]]>
代码缩进 2026-07-16T06:27:50+00:00 xulihang blog.xulihang.me/indentation-in-coding - Copy 代码缩进,使用4个空格或者制表符(Tab)表示一个层级,用来让代码有更好的可读性。而Python这样的语言,缩进是其语义的一部分,替代{}这样的括号。

有缩进的C语言代码:

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

没有使用缩进的C语言代码:

#include <stdio.h>

int main() {
printf("Hello, World!\n");
return 0;
}

现在一般都推荐用空格来缩进。因为使用tab,在不同编辑器,不同偏好设置的表现会不一样。有的人用两个空格宽度显示tab,有的人喜欢四个空格宽度。

代码编辑器一般都有自动调整缩进的功能。比如Visual Studio Code,使用SHIFT+ALT+F,就可以按偏好设置统一缩进,比如将四个空格缩进的改成两个空格缩进。

]]>
高跟鞋 2026-06-13T13:22:50+00:00 xulihang blog.xulihang.me/high-heels 穿高跟鞋可以美化小腿形状、增高、抬头挺胸,增加穿着者的自信,但存在健康问题。最早高跟鞋是男人穿的,现在变成女性的专属。

高跟鞋可以突显女性的魅力,听着女人哒哒哒的脚步声,男人的心也为之一荡。和胸、屁股、长发等共同组成了女性的特质。

穿高跟鞋对脚踝能力要求比较高。随着奥戴丽好笨这样博主的出现,穿高跟鞋跑步、跳跃也成了杂技一般的表演。

]]>
蜡笔小新 2026-06-13T12:56:50+00:00 xulihang blog.xulihang.me/crayon-shin-chan 最近在b站上看了不少《蜡笔小新》,还挺有意思的。

它是一部家庭动画,讲述了孩子还上幼儿园时,一家三口(后来加上小葵和小白就是五口)的日常故事。

比如妈妈和儿子出门,结果忘带钥匙,被关在门外。妈妈听到电话响,脑补是老公出事了,用石头砸开玻璃窗;小新出门,碰到陌生大龄未婚姐姐一起躲雨。本来这位姐姐不想搭理他,最后开车送小新回家;幼儿园吉永老师一直在等待男友表白,一帮幼儿园小朋友送上助攻。

可以说是把小孩日常生活中各种场景都拍出来了,比很多知名动漫更接地气点。

]]>
AI时代还有必要写博客吗 2026-06-13T12:44:50+00:00 xulihang blog.xulihang.me/is-blog-still-meaningful-in-the-AI-era 我的工作是写技术博客,优化网站SEO,让用户找到我们。那在AI时代,可以直接问AI了,还有必要写博客吗?

我之前写的博客,其实很多是某个技术具体的应用,比如如何用哪个Linux命令、怎么编译docbook文档。此外,还有很多常识的记录,比如记录某个疾病,可以用什么药物。这种的确问AI更方便了。但博客还是可以当作一种笔记存在,方便自己记忆和查询。

而其它很多随笔,凝聚着作者的情感和经历,是AI没有办法替代的。我看十年前的博客,就像是在跟十年前的自己对话一样。

另外,为什么写博客,要选择什么题目,也主要体现人的智慧和主观能动性。

]]>
暧昧 2026-06-13T12:27:50+00:00 xulihang blog.xulihang.me/ambiguous 今天看江祖平在《小明星大跟班》综艺节目的关于结婚还是不结婚的辩论,她觉得不结婚可以保持神秘感,享受暧昧,而同居的话会原型毕露,难以保持吸引力。

暧昧这个词经常被提到。它原意是光线昏暗,现在主要是指一种模糊的感受,特别是男女之间。通常是双方还没有明确心意的时候。

她经常看我,是不是喜欢我?她让我帮忙,但其实不是只有我才能帮,是什么意思?在这个过程中会形成双方之间的一种情感。

我其实不太喜欢暧昧的关系,我喜欢清楚明确的关系,但有时候喜欢的人却只想搞暧昧。暧昧产生的心动,其实和一个陌生人都很容易产生,并不是真正的适合,缺少深度的链接。这个时候,还是要坚决地将一切继续暧昧的可能斩断。

]]>
火车 2026-06-13T11:49:50+00:00 xulihang blog.xulihang.me/train 这周乘火车从杭州回海宁,看到去年乘的C406列车到杭州东的时间从19点55提前到了19点06,始发站从衢州改成了诸暨,正好无缝衔接我下班时间,半小时到海宁只要16元。果断订了回家的票。

绿色的动集,主要乘客应该都是打工回家的人,车厢里十分安静,不像之前暑假乘的高铁,小孩还是成人都吵闹得很。

在海宁站的高站台下车,出站,看到几十年的车站,和从前变化不大,出口有些饮食店,车站的广场有群众在跳舞。车站对面是老皮革城,但老早已经空无一人。老硖石的市中心也还是小时候的样子,但也显得很安静。

海宁站的确承载了一代代人的记忆,小时候去杭州、上海玩都是乘火车,上大学去无锡还是北京,都是买的卧铺。乘过空调特快、直达、特快、动车、高铁、动集,频繁去过南京南、无锡站、无锡东、北京站、北京南、杭州南、杭州东、杭州城站、上海虹桥、上海南等等各种车站。让我有迈出家门的感觉。

电影《爱在黎明破晓前》里,男主和女主就是在火车上相遇。虽然我不喜欢他们这种莫名做很多事情的形式,但的确火车上遇到一些人是挺有意思的事情。

我记得一次乘空调特快从上海回海宁,碰到一个小伙子,问我边上女生有没有充电宝,女生没有搭理,我说我有,他就坐我们位子对面。后来我才发现他是想加我边上女生微信。女生不搭理,就跟我聊,这样女生也能听着。他说他会读大专,想学计算机,然后说他去杭州,一天能拿好上千元。我问他做什么能拿那么多,边上女生似乎挺了解,直接说是试药,说这种很伤身体。然后我们就默默坐着了,小伙子一直想加微信,但没成功,我就下车了。

]]>
从杭州到汴州 2026-04-01T08:55:50+00:00 xulihang blog.xulihang.me/travel-to-henan 山外青山楼外楼,西湖歌舞几时休?暖风熏得游人醉,直把杭州作汴州。《题临安邸》这首诗虽然是讽刺当政者纵情声色,但也生动反映了两宋时期社会的繁华。

3月26日到3月30日,我随公司从杭州到河南郑州、洛阳和开封(汴州)旅游,这里对这次行程做个记录。

郑州

26日我们乘MU6993航班,于13点到达郑州,前往河南博物院进行参观。博物馆里有十几个展厅,讲述了中原大地从石器时代到清朝的历史。馆藏了从骨笛、铜鼎到陶瓷等各种器皿,我这个现代人都想象不出古人是怎么做出这种东西来的。知识没有得到应用,就会丢失,就像宝丽来的撕拉片,停产后有人买了之前的生产设备,但花了很久才在2010年代达到宝丽来70年代胶片的质量。

晚上我们去了住的大别山酒店不远处叫阿五黄河大鲤鱼的饭店,尝了些没吃过的菜。我看了眼微信的地图就骑车出发,结果搞错方向,我以后不嫌弃人家路痴了。吃好饭和同事一起走回酒店,北方春天的空气,带着点雾霾的味道,和北京的感觉还是比较像的。

回酒店,打开电视,也有不少没看过的频道。乐游频道在放英语纪录片《马背之旅》、动作电影频道在放《女子别动队》。

因为厚衣服带多了,只带了两件短袖,我每天都洗衣服,北方天气干燥的好处就是衣服晾几个小时就干了。

登封、洛阳

第二天出发前往登封的嵩山少林寺。少林武术很有名气,少林山脚有很多文武学校,小时候电视广告有招生宣传视频,这次是第一次实地看到文武学校和里面的学生。

我们参观了塔林和天王殿、大雄宝殿、藏经阁、立雪亭等少林寺的主要建筑。之后去练武堂看表演,但里三层外三层全是人,根本看不到。我只在后面看到了《十八般武艺》表演,然后表演结束,发现前门关了,让观众从出口走,会穿过比较长的商业区。

下午我们前往古都洛阳,参观了龙门石窟。龙门石窟景区是个大公园,中间是伊河水,两边是山,我们乘游览车进去,里面的石窟主要是北魏和唐朝时开凿,历经千年留存至今。

晚上去到洛阳东北较偏的银安美谷酒店。我在酒店周边逛了圈,有个市民健身场地还挺热闹,不少人在跳舞,不过边上的小区非常破旧,商业街看上去跟90年代的一样,路边都是路边摊,油烟味很重。出租车司机跟我说这里以前本来有些国企,后来不行了,人也不是很多了。相比热闹的洛阳市区真是两个世界。

第三天,去了上百公里外的海拔2000多米的老君山,乘云景索道上到中天门,再乘峰林索道,到十里画屏起点,之后一直上到山顶的金殿道观群。十里画屏风景还可以,有云海、奇石。环形步道上全是人,根本走不快,只有一段经过伏牛山主峰的路比较空。碰到两位阿姨说这么宽阔的地方适合放声喊,但她喊得太轻了,我就跟着大声喊了“喂”,想看看有没有回声。结果回声没听到,倒是另一个山头有人回喊。

晚上走高速回洛阳,看到了城市周边农村的样貌。一片片油菜花田包围下,是一幢幢平顶的二层房屋,每家门口都有明亮的白色的大灯。

回到洛阳城区,我们在应天门下车。现在的应天门是近年新建的,仿照隋唐时的应天门,非常壮观。门口和后面的广场,满是古代妆造的人,对着补光灯和镜头拍照。

和应天门隔了两站地铁路,是洛邑古城,里面是明清时期风格的建筑,有很多汉服妆造和拍照的店,穿汉服的人也非常多。印象比较深的是女生穿的襦裙,上身一件较短的直领的衣衫,胸口敞开,下身是一条裙子,通常是齐胸的。梳着发髻,化着妆,还搭配了各种饰品。不过传统服装要是能融入现代日常生活要更自然一点,比起摆拍,我更喜欢看穿着汉服骑电动车的小姐姐。

汉服女子

第四天,我们去了中国第一座佛教寺庙白马寺和边上的牡丹园。白马寺里面除了传统中式的庙宇,还有泰国、缅甸和印度的寺庙,是对外交流的前沿。其中印度寺庙的走廊还有各种佛教相关的艺术品。其中一幅画像列出了人生的四大苦难:生老病死。Amy说佛教就是让我们知道,虽然人生是来受苦的,但我们通过积极修行,可以苦中作乐。佛学还是有很多启发人的地方的。

牡丹是洛阳的市花,洛阳的牡丹品种多,花开得也很大,牡丹还被做成牡丹饼,可以吃。就相当于我们浙江的桂花糕?

开封

离开洛阳,我们前往开封。开封也是八朝古都,地下都是古城遗址。《清明上河图》、《东京梦华录》等著名的作品就是描绘的北宋时开封的场景。

我们去了万岁山大宋武侠城。这是一个主题公园,里面有水浒区、仙侠区、江湖艺术区,每天都会上演不同的节目,还有NPC能和游客一起玩游戏。比较出名的有王婆说媒、十字坡断头舞、二娘等等,还有三打祝家庄、打铁花这两个需要预约的节目。可能古时候的勾栏瓦舍就是这样?集商业、餐饮与文艺娱乐于一体的综合性娱乐中心。不过对于我这样的i人不是很友好,而且有点乱,走到哪看到哪。

第五天上午,我们去了开封府,也是复建的。开封府即是当年首都东京的首府衙门,包拯办案的地方。9点钟有开门表演,9点半有铡美案表演。演员都是有点年纪的,应该是4050再就业群体,然后围观的游客年纪也不小,还问我是不是河南大学的学生。

离开开封,我们就回到郑州新郑机场,乘飞机回杭州了。

感谢公司组织旅行,让我再次看到了我们大名同学不一样的一面,也从旅行和大名同学身上学习了很多。

]]>
年前最后一天班 2026-02-21T08:55:50+00:00 xulihang blog.xulihang.me/last-work-day-before-Chinese-new-year 2025农历最后一周是连着的6个工作日,不少人最后几天班都请假了。因为可以请假,大家不是一起上完年前最后一天班。

像我们软件公司工作相对不是很辛苦,工作环境是写字楼,明亮宽敞的室内,最后一天也不会有很多活,还会发坚守岗位红包。但我觉得已经无心工作了,最后一天的便宜不占也罢,直接请假了。

但其他的工作岗位,是满足人们日常所需的,或许我会坚持,甚至春节也上班。

我对工作意义的追求还是如此强烈。

]]>
过年 2026-02-21T08:08:50+00:00 xulihang blog.xulihang.me/celebrate-Chinese-new-year 2026年的春节,因为闰6月而比较晚。今天是初五,年也过得差不多了,稍微记录下过年的感受。

每年都要过年,走同一遍流程,但其实还是挺有意思的,就像好久没看的电视剧,时不时重温一下。

小时候的过年比较有意思点,那时候还没有汽车,爸爸骑着雅马哈摩托车载着我和妈妈从海宁市中心到湖塘乡下过年,与爷爷奶奶、叔叔婶婶和堂妹吃年夜饭,晚上住在80年代建的农村的二层房子里,一起看看春晚,到0点,各家会放开门炮仗。年初一起来,吃上一碗年糕粥,和妹妹们玩耍。年初二回外婆家,之后几天也会走亲戚和吃年酒,去到不同的村子。年前还会有请土地和拜太太(去世的长辈)这些仪式,准备草鱼、咸肉、酒盅,倒三次酒、拜上三次。再到正月十五,海宁硖石会有元宵灯会,这年也算彻底过完了。

长大后到现在,其实我过年都是在重温这种感觉。不过随着年纪渐长,时过境迁。老家农村的房子拆了,都搬到了商品房或者自建的统一规划的联排的屋子了。我上班了,妈妈也退休了。小时候的感觉就没有那么容易重温了。

这次我去舅爷爷家吃年酒。他们村的房屋建于2000年代,倒还有老底子的感觉。房屋前是一块大的晒场,中央一扇门,里面摆着一张八仙桌,楼梯位于两边,楼梯下方有厕所和杂物间。房屋延申出来一间平房,用作厨房,里面有灶台,可以点柴火烧饭,灶台上有烟囱。房屋边上有一口井,井边有一个台子,可以洗菜、洗衣服。房子边上就是广袤的农田,种着各种蔬菜、小麦或者桑树等植物。

不过没有看到有养牲口:鸡鸭、兔子、猪、羊。嘉兴其实一直都是养猪的,但后来因为污染禁养了。

过年,不同代的人团聚在一起,的确会存在各种冲突,但好在基本能做到和睦相处。也能感受到长辈的不容易。以后都是年轻人当家,怕是没有传统的这种年味了,要创造不一样的年味。

]]>