跳到主要内容

关于台湾进出口临时政策与历史数据的研究

· 阅读需 10 分钟

TODO: change this to reference (now it can't since the docusaurus can't parse the router)

see: ./src

进出口数据核验

起因是这张图:

picture 2

由于该博指出了数据来源:海关总署,所以出于对数据准确性的兴趣,我也小小核验了一下。

天然砂出口数据

首先给出第一个结论,关于天然砂的判断完全错误,大陆并非没有向湾湾出口天然砂。

当然,你在海关总署找到这个品类确实不是一件容易得事情,这主要是因为这个网站做的相当糟糕,不支持模糊搜索,也不说明字段设计(尽管可能是国际通用)。

你首先得知道商品编码分四个类级,分别采用2、4、6、8位数字,意思就是每一级最多有100个子类(完全够用了),然后天然砂(如果我们搜索没错的话)是属于第二大类,编码为2505(此外,还有一个叫“其他天然砂”的第三大类,编码为250590)。

picture 3

出口如下,平均每个月100w人民币左右:

picture 11

而且还没有1-3月数据,所以再看一下去年的数据:

picture 12

基本是一年1kw左右。

冰鲜带鱼进口数据

图中提到的冰鲜带鱼其实应该是其子类“冰鲜白带鱼”:

picture 9

但不管了,我们允许向上估计,同样的流程,我们搜索出比较对标的编码是 03028910,03038910 :

picture 8

结果如下:

picture 13

大概每个月1600w左右,1-6月总值确实与其计算的3937w接近。

picture 14

冻竹荚鱼进口数据

同上处理,得到的数据也接近。

结论

尽管天然砂出口湾湾的量并不大,还不及这几条鱼,但显然,互联网的信息传播依然很经不起推敲。

但如我和大家讨论的,我更关心天然砂对湾湾的产能影响:

picture 15

因为我找到了某个媒体新闻,说湾湾依赖大陆的天然砂达90%(2021年):

picture 16

并称之为“捏到了台湾半导体制造业的七寸”。

那是否果真如此呢?

台湾天然砂进口分析

与大陆系统对比分析

这一次,我们需要用到台湾的海关总署数据了。

这个平台做的就远比国内的海关总署数据用心多了,体验好非常多,前面那个就属实摆烂(国内政企saas的通病,故意让你用的不爽)。

不过除此使用,则会有意外的感受,那就是年份是从92到111,我脑子迅速地过了一下,2022-111=1911,好家伙,原来是以民国元年为第一年计算的……

picture 17

不管这个啦,继续我们的研究。

刚刚我们说了编码是通用的,天然砂的编码是2505,所以我们直接输就可以。

可以看到,湾湾的软件就可以对出口和进口同时分类显示,这才是正常的设计:

picture 18

然后只有美元和新台币两种计价单位,为了核算,我们一律使用美元,然后再把之前的国内海关署的台湾出口天然砂数据拉一下对比看看:

picture 19

可以看到,国内显示对湾出口天然砂4月有16.17万美元,5月有15.015万美元,而湾湾显示对大陆进口天然砂个月有34.3万美元,5月有30.7万美元。

对不上。

不过注意,湾湾表示,这些字段是包含复进口的,所以多一点也许也可以理解。

不过令我最不解的是,国内只有个别月有数据,这显然是不正常的。

所以,假如湾湾是客观正确的,那么国内数据系统没有错的话,那就是人为降低了数据量。

那国内为啥会少报出口量呢,好像也没有道理,所以权当是数据系统错误吧。

反正那个系统(除了验证码都是准确的之外),你也不能指望能准到哪里去。

大陆占比分析

接下来研究湾湾进口天然砂的大陆占比,是否果真达到了90%。

数据上,由于湾湾的海关数据系统做的非常好,所以我们很方便地能拿到湾湾从民国92年(2003)到111年(2022)从世界各国进口天然砂的数据:

picture 20

然后我们只需要按年分组,分别求得中国进口金额、全球总计进口金额与其两者的比值,最终得到结果如下:

picture 22

很遗憾的是,06年及以前,台湾对大陆的天然砂确实依赖程度高达90%,但是09年之后就断崖式下跌,19年之后已完全在5%以下……

无话可说。

台湾历年进出口数据国别分析

最后,趁着这个机会,研究一下台湾对各国进出口的情况。

由于年份和国家众多,想展示趋势并不太容易,所以敲定的方案就是选择十个典型国家,其他的用“others”去合计。

而这十个典型的国家,基于2021年湾湾进口金额前十的国家,它们依次是:

picture 28

值得注意的是,前十的总和也只有 75%,说明湾湾的多边合作做的还是不错的。

最后直接上图吧:

picture 23

picture 24

picture 25

以上!

代码

代码一:台湾天然砂进口分析

import re

import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
from matplotlib.ticker import ScalarFormatter, PercentFormatter

plt.rcParams["figure.dpi"] = 320
plt.rcParams['font.sans-serif']=['Heiti TC', 'Songti SC']

### step 1. data fetch

# 台湾海关数据查询网址: https://portal.sw.nat.gov.tw/APGA/GA30
# 参数设置:货品(天然砂):2505
df = pd.read_excel("~/Downloads/綜合查詢_20220803143014.xls")


### step 2. data wash

def china_with_world(df: pd.DataFrame):
china = float(df[df['國家'] == '中國大陸']['美元(千元)'])
total = df['美元(千元)'].sum()
pct = china / total
return pd.Series({
"total": total,
'china': china,
'pct': pct
})

df['year'] = df['日期'].apply(lambda x: int(re.search(r'\d+', x).group()) + 1912 - 1)
df2 = df.groupby(['year']).apply(china_with_world).reset_index()
display(df2)


### step 3. data visualization

# scientific formatter, ref: https://atmamani.github.io/cheatsheets/matplotlib/matplotlib_2/#Numbers-on-axes-in-scientific-notation
formatter = ScalarFormatter(useMathText=True)
formatter.set_powerlimits((0, 1)) # https://matplotlib.org/3.4.3/api/ticker_api.html#matplotlib.ticker.ScalarFormatter.set_powerlimits

# two kinds draw from dataframe: https://www.statology.org/matplotlib-two-y-axes/
ax1 = host_subplot(111)
ax1.yaxis.set_major_formatter(formatter)

ax2 = ax1.twinx()
ax2.yaxis.set_major_formatter(PercentFormatter(1))

index = df2.year.astype(str)
ax1.bar(index, df2.total, label='全部')
ax1.bar(index, df2.china, label='大陆')

ax2.plot(index, df2.pct, 'g-o', linewidth=2, label='大陆占比')
for i,j in zip(index, df2.pct):
ax2.annotate(f'{j*100:.0f}%',xy=(i,j), color='magenta')

plt.figtext(.5,.99,'台湾天然砂进口数据', size=12, ha='center', va="top")
plt.figtext(.9,.92,'data source:https://portal.sw.nat.gov.tw/APGA/GA30', size=8, ha='right', color='gray', style='italic')
plt.figtext(.9,.89,'design: markshawn, Aug 2, 2022', size=8, ha='right', color='gray', style='italic')
plt.figtext(.18,.895,'美元', size=8, style='italic')
plt.legend()
plt.xticks(rotation = 45); # Rotates X-Axis Ticks by 45-degrees

代码二:台湾历年进出口分析

# 数据:https://portal.sw.nat.gov.tw/APGA/GA30_LIST

def preprocess(fp) -> pd.DataFrame:

df = pd.read_excel(fp)
df = df.rename(columns = {
'進出口別': 'io',
'日期': 'year',
'國家': 'country',
'貨品號列': 'good_kind_id',
'中文貨名': 'good_kind_name_cn',
'英文貨名': 'good_kind_name_en',
'美元(千元)': 'amount_k'
})
df['year'] = df.year.apply(lambda x: int(re.search('\d+', x).group()) + 1912 - 1)
return df


def addTotalRow(df: pd.DataFrame):
totalRow = df.sum(numeric_only=True).to_frame(name='Total').T
return pd.concat([df, totalRow])


def calcDir(df: pd.DataFrame):
s = df.groupby('io').sum()['amount_k']
s['顺差'] = s.get('出口總值(含復出口)', 0) - s.get('進口總值(含復進口)', 0)
return s

def getTopCountries(df, io="進口總值(含復進口)", year=2021, N=10):
dff = df.query(f'io=="{io}" and year=={year}')
dfs = dff.sort_values('amount_k', ascending=False)
dfs.set_index('country', inplace=True)
total = dfs.amount_k.sum()
print('total: ', total)
dfs['pct'] = dfs.amount_k / total
return dfs[:N]


def squashCountries(df: pd.DataFrame):
cur = df.query(f'country in {topCountries}')
sumOthers = df.query(f'country not in {topCountries}')['amount_k'].sum()
newRows = pd.DataFrame([{"country": "others", "amount_k": sumOthers, 'io': df['io'].iloc[0]}])
result = pd.concat([cur, newRows])
return result


def handleYear(df):
return squashCountries(df).set_index('country')['amount_k']


def plotTrend(df, name=None):
df.plot(kind='bar', stacked=True);
plt.legend(bbox_to_anchor =(1, 0.5), loc='center left');
plt.xlabel(None)
if name:
plt.suptitle(name)
plt.show();


def handleIO(df):
df2 = df.groupby('year')\
.apply(handleYear) / 1e8 * 1e3
plotTrend(df2, df.name)

def showTop(dfTopCountries):
x = addTotalRow(dfTopCountries)
x.columns.name = str(int(x.year.iloc[0])) + "-" + x.io.iloc[0]
x.drop(columns=['year', 'io'], inplace=True)
x['金额(亿美金)'] = x.amount_k / 1e8 * 1e3
x['全球占比'] = x.pct.apply(lambda x: f"{x*100:.2f}%")
display(x.drop(columns=['amount_k', 'pct']))

df = preprocess('/Users/mark/Downloads/綜合查詢_20220803190515.xls')
df2 = df.groupby(['country', 'year']).apply(calcDir).reset_index()
dfTopCountries = getTopCountries(df2)
showTop(dfTopCountries)
topCountries = dfTopCountries.index.to_list()

df2\
.groupby('io')\
.apply(handleIO)