Excel 后,我们需要怎样的数据分析软件

在现代商业环境中,数据分析已成为企业决策的重要工具。通过数据分析,企业可以更好地了解市场趋势、客户行为以及内部运营情况,从而制定出更科学的策略,提高竞争力。然而,数据分析并不是一项简单的任务,需要选择合适的工具和方法。

很多人认为 BI 软件是数据分析的首选,因为 Business Intelligence 这个词听上去和数据分析的关系很密切,而且 BI 软件通常具有流畅的交互性和炫丽的界面,看上去很适合做这项工作。然而,BI 工具在技术上已经被狭义化成多维分析,主要用于基于预设的数据立方体的汇总和展示。这在一些简单场景中的确有效,比如:企业成本过高时精确定位出是哪个时间段和哪些部门导致的。但面对复杂的业务问题时,BI 软件往往显得力不从心。比如,股票分析师猜测:满足某种条件的股票会更容易上涨?销售总监考虑:安排哪类销售员去对付难度大的客户会更有效?电商的数据分析员判断:以较低成本奖励符合某种特征的用户,就能获得高出平均值的收益。

这些问题都涉及到多步骤交互式计算,是传统 BI 工具无法应对的。

看具体例子:股票分析师根据自己的经验大胆做出猜测,符合某种预设条件的股票将来应该更容易上涨。为了验证自己的猜想,他先针对历史数据进行计算,找到符合这些条件的股票。再看这些股票的走势,如果大部分真如猜测一样上涨了,那就验证了猜测;如果猜测不对,那就调整猜测,改变约束条件,继续用历史数据验证猜测。经过对预设条件的多次迭代,最终使筛选出的股票达到较高的上涨概率和涨幅,就能得到一种规律性的结论,可以据此指导未来的股票交易。以上就是用多步骤交互式计算完整解决一个数据分析任务的过程, 本质上,就是基于历史数据反复猜测和验证,最终获得某种规律。

1png

有经验的业务人员会提出猜测,剩下要做的是帮助业务人员验证猜测,也就是对历史数据做计算。这种计算的特点在于:不能事先设计过程,分析人员会根据上一步的结果临时决定下一步动作,也就是自由操纵计算过程。这就是交互式计算,非常类似于使用计算器。但与普通的数值计算器不同的是,数据分析需要计算的数据不是简单数值,而是批量的表格,也就是结构化数据。这种能力可以形象地称为表格型数据计算器

2png

Excel 就是这样一个表格型数据计算器。作为一种广泛使用的数据分析工具,Excel 具有良好的交互性,适合处理表格型数据,甚至,BI 的多维分析功能也包含在 Excel 透视表内了,只是界面的炫丽度和操作流畅性要弱一些。事实上,Excel 是业务人员最常用的数据分析软件。

但是,用 Excel 做数据分析久了,困扰也来了,有些工作用 Excel 很难完成。

数据太大跑不动。Excel 有容量限制,数据量一大就跑不动,几十万行就会很慢,超过百万行就直接没法工作了。

公式太难写不出。集合运算和分组后运算也是 Excel 不擅长的,比如,前面提到的“更容易上涨的股票”,验证猜测中的某一步可能是:找出股票连续上涨 5 天以上的区间,看看是否有某种规律。

3png

这个任务会涉及有序分组并保持分组子集用于筛选,Excel 很难应对这些较复杂的数据计算。

数据库可以解决 Excel 的问题吗?数据库应付稍大的数据量是没问题的,但环境准备复杂得多,而且 Excel 还要经过繁琐的入库才能分析。最重要的,SQL 难度经常远大于 Excel,比如计算每支股票最长连涨天数,用 SQL 要写成这样:

SELECT CODE, MAX(con_rise) AS longest_up_days
FROM (
    SELECT CODE, COUNT(*) AS con_rise
    FROM (
        SELECT CODE, DT,  SUM(updown_flag) OVER (PARTITION BY CODE ORDER BY CODE, DT) AS no_up_days
        FROM (
            SELECT CODE, DT, 
                    CASE WHEN CL > LAG(CL) OVER (PARTITION BY CODE ORDER BY CODE, DT)  THEN 0
                    ELSE 1 END AS updown_flag
            FROM stock
        )
    )
    GROUP BY CODE, no_up_days
)
GROUP BY CODE

这种嵌套的 SQL 代码,专业程序员都很难写出来。

这个任务用 Excel 反而很简单。第一步按照股票代码、日期排序;第二步填写公式计算连续上涨天数;第三步分组汇总计算最长连续上涨天数;第四步收缩显示。直观四步搞定。

4png

BI当然也指望不上。BI 本身用起来不难,但它解决大数据还是要依托数据库,同样还会面临环境准备的困难。而且,如前所述,狭义化成多维分析的 BI,计算能力简化到远不如 SQL 了,连刚才那个股票最长连涨天数都算不出,Excel 熟手会感到很受限。

看来,似乎只能借助于编程了,毕竟没有什么不能编程完成。

但是,大部分编程语言交互性差,离表格型数据计算器的概念较远,也不适合做数据分析软件。VBA可以增强 Excel 的计算能力,但对结构化数据支持力度太差,编程任务过于繁琐。而且 VBA 没有内置的大数据能力,只能自己实现,更加繁琐。

满大街培训班都在喊的Python总可以了吧?Python 的表格运算能力还行,但是写起来还是有点复杂,比如计算股票连涨天数要写成下面这样,要硬写循环。

import pandas as pd
stock_file = "StockRecords.txt"
stock_info = pd.read_csv(stock_file,sep="\t")
stock_info.sort_values(by=['CODE','DT'],inplace=True)
stock_group = stock_info.groupby(by='CODE')
stock_info['label'] = stock_info.groupby('CODE')['CL'].diff().fillna(0).le(0).astype(int).cumsum()
max_increase_days = {}
for code, group in stock_info.groupby('CODE'):
    max_increase_days[code] = group.groupby('label').size().max() – 1
max_rise_df = pd.DataFrame(list(max_increase_days.items()), columns=['CODE', 'max_increase_days'])

此外,Python 也没有直接的大数据支持,也要自己写,这远远超出了 Excel 选手的能力范围。

而且,这些编程语言的交互性也不好,必须写完代码执行后才能看到结果,发现有局部错误也要改正后再从头执行,这远没有所见即所得的 Excel 顺手。

这些看似热门的编程语言并不是好选择,还有什么别的吗?

还有esProc Desktop,后 Excel 的数据分析神器。

esProc Desktop 是一个表格型数据计算器,提供了面向结构化数据的编程语言 SPL,开发环境有极强的交互性,可以方便高效地处理大数据,可以增强 Excel 的计算能力,极大地降低了复杂计算的难度,是数据分析人员的理想工具。

所见即所得的强交互计算能力

SPL 的网格编程就像是写 Excel 公式,业务人员也能轻松掌握。格名就是天然的变量名,可以直观引用之前的计算结果。SPL 代码可以单步执行,随时在右侧查看计算结果,结构化数据自动以表格形式展现,特别适合多步骤的交互式计算。

以前面的股票连涨区间为例,SPL 代码分为五步:读文件、排序、有序分组、过滤、合并,交互过程可以清晰地呈现在格子里。

5png

每步都可以独立执行,有错误时可以只修改局部代码重新执行这一句再观察结果。

远超 Python 的表格计算能力

SPL 有强大的表格运算能力,代码比 Python 简单很多,既具备 Excel 的简单性,又能解决 Excel 难算的任务,真的做到既要又要。比如计算每支股票最长连涨天数,简单三行即可搞定。


A

1

StockRecords.xlsx

2

=T(A1).sort(DT)

3

=A2.group(CODE;~.group@i(CL<CL[-1]).max(~.len()):max_increase_days)

更复杂的任务:求每 7 天中连续 3 天活跃的用户数。Python 写起来很罗嗦:

df = pd.read_csv("../login_data.csv")
df["ts"] = pd.to_datetime(df["ts"]).dt.date
grouped = df.groupby("userid")
aligned_dates = pd.date_range(start=df["ts"].min(), end=df["ts"].max(), freq='D')
user_date_wether_con3days = []
for uid, group in grouped:
    group = group.drop_duplicates('ts')
    aligned_group = group.set_index("ts").reindex(aligned_dates)
    consecutive_logins = aligned_group.rolling(window=7)
    n = 0
    date_wether_con3days = []
    for r in consecutive_logins:
        n += 1
        if n<7:
            continue
        else:
            ds = r['userid'].isna().cumsum()
            cont_login_times = r.groupby(ds).userid.count().max()
            wether_cont3days = 1 if cont_login_times>=3 else 0
            date_wether_con3days.append(wether_cont3days)
    user_date_wether_con3days.append(date_wether_con3days)
arr = np.array(user_date_wether_con3days)
day7_cont3num = np.sum(arr,axis=0)
result = pd.DataFrame({'dt':aligned_dates[6:],'cont3_num':day7_cont3num})

SQL 更加冗长:

WITH all_dates AS (
    SELECT DISTINCT TRUNC(ts) AS login_date
    FROM login_data),
user_login_counts AS (
    SELECT userid, TRUNC(ts) AS login_date, 
    (CASE WHEN COUNT(*)>=1 THEN 1 ELSE 0 END) AS login_count
    FROM login_data
    GROUP BY userid, TRUNC(ts)),
whether_login AS (
    SELECT u.userid, ad.login_date, NVL(ulc.login_count, 0) AS login_count
    FROM all_dates ad
    CROSS JOIN (
        SELECT DISTINCT userid
        FROM login_data) u
    LEFT JOIN user_login_counts ulc
    ON u.userid = ulc.userid
    AND ad.login_date = ulc.login_date
    ORDER BY u.userid, ad.login_date),
whether_login_rn AS (
    SELECT userid,login_date,login_count,ROWNUM AS rn 
    FROM whether_login),
whether_eq AS(
    SELECT userid,login_date,login_count,rn,
        (CASE WHEN LAG(login_count,1) OVER (ORDER BY rn)= login_count 
        AND login_count =1 AND LAG(userid,1) OVER (ORDER BY rn)=userid 
        THEN 0 
        ELSE 1 
        END) AS wether_e	
    FROM whether_login_rn),
numbered_sequence AS (
    SELECT userid,login_date,login_count,rn, wether_e,
    SUM(wether_e) OVER (ORDER BY rn) AS lab
    FROM whether_eq),
consecutive_logins_num AS (
    SELECT userid,login_date,login_count,rn, wether_e,lab,
        (SELECT (CASE WHEN max(COUNT(*))<3 THEN 0 ELSE 1 END)
        FROM numbered_sequence b
   	WHERE b.rn BETWEEN a.rn - 6 AND a.rn
	AND b.userid=a.userid
   	GROUP BY b. lab) AS cnt
    FROM numbered_sequence a)
SELECT login_date,SUM(cnt) AS cont3_num
FROM consecutive_logins_num
WHERE login_date>=(SELECT MIN(login_date) FROM all_dates)+6
GROUP BY login_date
ORDER BY login_date;

SPL 代码要简短易懂得多,普通业务人员也能轻松掌握:


A

1

=T("login_data.csv")

2

=periods(date(A1.ts),date(A1.m(-1).ts))

3

=A1.group(userid).(~.align(A2,date(ts)).(if(#<7,null,(cnt=~[-6:0].group@i(!~).max(count(~)),if(cnt>=3,1,0)))))

5

=msum(A3).~.new(A2(#):dt,int(~):cont3_num).to(7,)

天然的大数据支持

SPL 提供了原生的大数据处理能力,支持游标以应对超出内存容量的外存计算,内外存计算代码几乎一致,不会额外增加工作量。比如,计算每支股票最长连涨天数,只要把 import 函数改为 cursor。


A

1

StockRecords.txt

2

=file(A1).cursor@t().sortx(CODE,DT)

3

=A2.group(CODE;~.group@i(CL<CL[-1]).max(~.len()):max_increase_days)

SPL 支持基于线程的并行计算,充分利用多核 CPU。比如,找出股票连涨超过 5 天的区间,只要增加一个 @m 选项,性能直接起飞。


A

1

StockRecords.txt

2

=file(A1).cursor@tm().sortx(CODE,DT)

3

=A2.group@i(CODE!=CODE[-1]||CL< CL[-1])

4

=A3.select(~.len()>=5).conj()

内嵌 Excel 的增强插件

esProc Desktop 提供了 XLL 插件,用户可以在熟悉的 Excel 环境中,利用 SPL 的强大计算能力,在 Excel 内直接写 SPL 公式,同时发挥 SPL 和 Excel 优势。比如连涨区间的计算,在 Excel 里写一句 SPL 代码就能算完。

6png

再比如:找出每个月都能进 top10 的明星产品,Excel 做交集很麻烦,SPL 集合运算要强大得多,简短的公式可直接获得结果。

7png

esProc Desktop 现在可以免费使用,还有图书、课程、Excel 例程集等丰富的资料,详见https://www.raqsoft.com.cn/desktop-download