
简介这份压缩包整合了1946—2023年基于联合国投票记录估计的国家理想点距离数据面向国际关系、政治学及计量研究领域的师生与研究者可用于分析国家偏好距离、外交立场演变及多边合作趋势无需自行爬取和处理投票数据。压缩包共含7个文件约46.95MB设计较为完整两份CSV数据文件适合直接导入常见统计软件R数据文件和Stata数据文件分别对应R与Stata环境另附数据来源说明网页、变量说明书Word文档和估算方法论文PDF方便不同工具链用户读取与复现。已有59人学习浏览适合需要快速获取可用数据集完成论文、报告或课堂项目的用户。其中“与中国理想点距离”的Stata数据文件可直接用于中外双边关系研究Codebook与配套论文详述了变量定义、样本范围和估算逻辑帮助用户规范引用并快速上手。1. 理想点距离1946-2023年.zip 是什么一个能直接当决策依据的时间序列数据包做过多指标评价的人看到这个名字应该会很眼熟理想点距离是 TOPSIS逼近理想解排序法里的核心中间结果每个评价对象到正理想解和负理想解的欧氏距离决定了它最终的综合得分和排名。而这个压缩包把 1946 到 2023 年共 78 个年份的理想点距离按年组织成了一组数据文件等于把几十年评价计算的底稿直接递到了你手上。它的价值在于不用重新找原始指标、不用重复跑规范化流程拿到手就能做趋势分析、排名复核和年报出数。适合正在做综合评价、指标体系建设、历史数据回溯或者需要给报告配“得分走势”的从业者时间序列研究者也可以把它当作现成的面板数据样本。2. 拿包先别解压校验、伪加密与正确解压2.1 为什么压缩包先校验再解压很多人的习惯是双击压缩包直接“解压到当前文件夹”遇到几百 MB 的数据包更是懒得等校验。但理想点距离这种跨 78 年的数据包里面有大量 csv、xlsx 或 dbf 文件任何一个文件在传输过程中损坏后续按年拼接时都会出现“某一年数据对不上”的诡异问题。usb 拷贝、网盘下载、邮件附件三种常见途径都可能导致 zip 的中央目录和本地文件头不一致。解压后再发现缺文件回去重新下载整个包的成本更高。所以我的固定动作是先看压缩包完整性再看包内文件清单最后才真正释放文件。这一步在 Linux 和 Windows 上都有现成工具但命令和参数不一样下面分开说。2.2 用 unzip 与 zipinfo 做三项体检的命令在 Linux 或 macOS 终端里拿到“理想点距离1946-2023年.zip”之后我一般会依次跑三条命令# 1. 测试压缩包完整性不释放文件 unzip -t 理想点距离1946-2023年.zip # 2. 查看包内目录结构和文件总数 unzip -l 理想点距离1946-2023年.zip | head -40 # 3. 查看压缩方式、加密标志和文件时间戳 zipinfo -v 理想点距离1946-2023年.zip | grep -E ^[-a-z]$|file security status|compression method第一条unzip -t是测试模式逐文件读出 CRC32 和压缩包里的记录值做比对任何一条“bad CRC”都会在这里暴露。第二条unzip -l列出包内所有文件路径先看有没有多余的临时文件、空目录或者异常命名避免解压出预期之外的内容。第三条zipinfo -v是体检细节重点看file security status那一行显示encrypted说明文件有密码保护显示not encrypted才是干净的普通 zipcompression method显示deflated是常规压缩stored表示未压缩存储。参数说明-t是 test 的缩写不写输出文件-l是 list管道到head -40是因为年份表文件可能很多只看前 40 行够了grep的正则里^[-a-z]$匹配以字母开头的小节标题行file security status和compression method是 zipinfo 输出里的固定字段名。Windows 用户如果没有 Linux 环境用 7-Zip 的文件管理器打开包后按ShiftL可以测试压缩包或者打开命令行工具7z t 理想点距离1946-2023年.zip效果一致。注意不要用系统自带的“压缩文件夹”功能做测试它只验证能否列出文件不校验 CRC。2.3 伪加密与“密码移除”的误解zip 伪加密是流传很广的坑理想点距离这类公开数据包偶尔也会中招。现象是解压时提示输入密码但包里全是 CSV 数据表发布方也没提过加密。原因在于 zip 格式的加密标志位有两种一种是真正的 AES 或 ZipCrypto 加密另一种只是把全局加密标志位置 1数据本身并没有加密。后者就是伪加密解压工具看到标志位就会停下来要密码。遇到伪加密不需要做什么“密码移除”改标志位就行。Linux 下没有现成的单命令工具我常用的办法是先用zipinfo -v看每个文件的file security status如果确认是伪加密用 Python 直接改文件头里的加密标志字节import struct with open(理想点距离1946-2023年.zip, rb) as f: data bytearray(f.read()) count 0 for i in range(len(data) - 4): # 定位本地文件头 0x504B0304 if data[i:i4] bPK\x03\x04: flag_offset i 6 flags struct.unpack(H, data[flag_offset:flag_offset2])[0] if flags 0x0001: # 加密位为 1 flags ~0x0001 struct.pack_into(H, data, flag_offset, flags) count 1 with open(理想点距离1946-2023年-fixed.zip, wb) as f: f.write(bytes(data)) print(f修复了 {count} 个本地文件头的加密标志位)逻辑说明zip 每个本地文件头的第 6、7 字节是通用标志位最低位是加密标志。代码遍历整个文件找PK\x03\x04签名把加密位置 0 后写回新文件。参数说明rb是读写二进制模式H是小端无符号短整型0x0001是加密位的掩码struct.pack_into直接修改指定偏移的字节避免重建整个文件。需要提醒的是修改标志位只适用于你确认这份数据应当公开、没有版权争议的情况。如果发布方明确声明了密码那属于正规加密不该绕过你要做的是联系数据提供方索取授权。另外真正 AES 加密的 zip改标志位没有任何作用解压时依然会报错那种情况要么有密码要么就放弃这份数据换个来源。3. 把包内数据读成可用结构目录认识与 Python 标准读入3.1 包内“一年一表”的常见组织方式跨年度的理想点距离数据包目录结构通常遵循“按年份分文件、按区域或对象分行、按指标分列”的约定。我经手过的类似数据包典型布局是这样理想点距离1946-2023年/ ├── 1946.csv ├── 1947.csv ├── ... ├── 2022.csv ├── 2023.csv ├── indicator_definition.xlsx ├── weight_config.json └── README.txtREADME 里一般写清楚了理想点距离的定义和计算口径正理想点距离 D 表示评价对象距离最优虚拟方案的距离负理想点距离 D- 表示距离最劣虚拟方案的距离综合贴近度 C D- / (D D-)。CSV 文件里除了 D 和 D- 两列往往还有评价对象的 ID、名称以及规范化后的各指标值。这一节的关键是解压后不要急着把 CSV 丢进 Excel 里看先确认列名、分隔符和编码。跨年份数据最容易翻车的地方就是早期年份用 GBK 编码近几年用 UTF-8直接读会报编码错误或中文乱码。3.2 用 pandas 读入并按年拼接我一般会用一个 Python 脚本把解压后的数据全部读进来统一编码、统一列名拼成一张长表import pandas as pd from pathlib import Path data_dir Path(理想点距离1946-2023年) frames [] for csv_path in sorted(data_dir.glob(*.csv)): year int(csv_path.stem) # 文件名就是年份 # 优先按 UTF-8 读失败就回退 GBK try: df pd.read_csv(csv_path, encodingutf-8-sig) except UnicodeDecodeError: df pd.read_csv(csv_path, encodinggbk) df.insert(0, year, year) # 每一行打上年份标记 frames.append(df) full_data pd.concat(frames, ignore_indexTrue) full_data.to_parquet(ideal_point_distance_full.parquet) print(full_data.groupby(year).size().head())逻辑说明脚本的核心是用年份文件名作为主键把所有 CSV 纵向拼接成一张“长表”之后按年份筛选、按对象追踪、画趋势线都在这张表上完成。utf-8-sig能自动吃掉带 BOM 的头部gbk是中文环境老数据最常见的编码。参数说明csv_path.stem取文件名的去后缀部分int()强转成年份数字df.insert(0, year, year)把年份插到第 0 列而不是追加到末尾方便一眼看到to_parquet是给后续分析用的没有 parquet 环境可以换成to_csv(full.csv, indexFalse)。读完之后马上做两个检查一是full_data.isna().sum()看有没有整列空值二是full_data.groupby(year).size()看每年行数是否在合理区间。如果某一年行数为 0说明那年的 CSV 可能是空文件回头用 2.2 的unzip -l对照包内文件列表确认是源数据缺失还是解压失败。3.3 复算贴近度验证数据完整性光能读进来还不够还要验证包里的理想点距离算得对不对。这是最容易被跳过、但最能发现问题的一步。TOPSIS 的贴近度 C 在每一年内部独立计算所以我可以从 CSV 里取出 D 和 D- 两列复算 C 值和包里给出的 C 列做比对import numpy as np for year, grp in full_data.groupby(year): d_plus grp[D].to_numpy() d_minus grp[D-].to_numpy() with np.errstate(divideignore, invalidignore): c_calc d_minus / (d_plus d_minus) c_given grp[C].to_numpy() diff np.nanmax(np.abs(c_calc - c_given)) if diff 1e-10: print(f{year} 年贴近度与包内值不一致最大差 {diff:.2e})逻辑说明C 的定义是负理想距离占正负距离之和的比例范围恒在 0 到 1 之间。用每行的 D 和 D- 直接相除再与包里现成的 C 列对比理论上浮点误差在 1e-10 量级。如果某年差值到 1e-5 以上基本可以断定那个年份文件的数据在生成或拷贝过程中出了问题。参数说明np.errstate是防止某行 D 和 D- 同时为 0 时除零报警np.nanmax过滤掉 NaN 后取最大偏差1e-10是浮点容差阈值实际使用时可以放宽到 1e-8。这一步跑通之后你就能确认这包数据的 D、D-、C 三列内部自洽。接下来无论是直接把 C 值作为评价得分出报告还是把原始 D 值接进自己的排名逻辑重算都有了可信的基础。顺带说一句如果包里没有 C 列而只有 D 和 D-这个脚本就变成了补算 C 列的工具逻辑完全一致。4. 从压缩包到业务数据源Linux 与 Windows 的落位方案4.1 Linux 离线环境的 zip 工具准备内网离线环境里解压数据包经常碰到“没装 unzip”的尴尬。Debian/Ubuntu 系用apt install unzipCentOS/RHEL 系用yum install unzip但在离线环境这两条命令都无效。我一般提前备好 deb 或 rpm 安装包或者更轻量地直接用 Python 的 zipfile 模块做解压省掉系统依赖# 离线环境用 Python 解压不依赖系统 unzip python3 - EOF import zipfile z zipfile.ZipFile(理想点距离1946-2023年.zip) z.extractall(ideal_point_output) z.close() EOF逻辑说明zipfile 是 Python 标准库有 Python 3 就有它不需要额外安装。extractall会把压缩包内所有文件释放到指定目录目录不存在会自动创建。参数说明第一个参数是压缩包路径第二个是输出目录名z.close()是显式释放文件句柄避免 Windows 下文件被占用导致后续操作失败。这个办法的代价是没有 CRC 校验输出所以释放完最好用 2.2 节的方法检查一遍。如果环境里连 Python 都没有那就只能在有网的机器上提前把 unzip 的离线安装包装好。确认方法很简单which zipinfo和which unzip两条命令都返回路径才说明工具链齐全。另外某些精简版 Linux 只装了zip压缩命令没装unzip解压命令别看到zip -?能执行就以为解压也没问题。4.2 Windows 下解压、便携化与右键压缩干扰Windows 用户拿到理想点距离数据包最常见的路径是右键 → 全部解压缩。系统原生 zip 功能对小文件没问题但 78 个 CSV 加配置文件一起解压时经常出现两个问题一是解压进度条走完但文件不完整二是右键菜单里误点了“压缩为 zip 文件夹”把原本正确的数据目录二次压缩导致文件层级多套了一层。我的建议是弃用系统自带功能装一个便携版解压工具。CrystalDiskInfo 那种“便携版 zip、免安装、解压即用”的思路套到压缩工具上同样适用7-Zip 便携版解压后直接运行7z.exe不需要管理员权限、不写入注册表适合在客户机器和管制环境里临时使用。命令示例:: 在 Windows 命令行中用 7-Zip 测试并解压 C:\path\7z.exe t 理想点距离1946-2023年.zip C:\path\7z.exe x 理想点距离1946-2023年.zip -oD:\data\ideal_point -y参数说明t是测试x是解压-o指定输出目录冒号后紧跟路径不能有空格-y是全自动覆盖文件。不加-y时遇到已存在的同名文件会交互式询问在脚本化批处理里会造成卡住。注意-o的语法比较特殊-oD:\data\ideal_point是一个整体如果写成-o D:\data会被 7-Zip 当成两个参数解压位置就不对了。解压后如果想在 Windows 下“便携化”使用——也就是不让文件散落在临时目录、不依赖 Excel 宏、双击即用——常见的做法是建一个run.bat放在数据根目录里把 Python 或数据分析脚本的调用固定下来让数据包变成一个可重复执行的分析单元。这种方式在团队内部流转时特别好用别人拿到数据包不用问“怎么打开”直接双击即可。4.3 把解压目录注册为服务数据源数据解压只是第一步真正要让它发挥作用是把这 78 年数据接进周期性的评价流程。一个常见的做法是把解压目录当作一个只读数据源挂到服务或者数据库里类似 MySQL 的 zip 包在 Windows 上手动设置成本地服务那样先解压到固定目录再写配置文件指向它最后注册成服务或定时任务。在 Linux 上我一般会用 systemd 的定时器或 cron 来处理“每月一次数据刷新”的诉求。下面是一个最小可用的 cron 配合脚本# /etc/cron.d/refresh_ideal_point 0 2 1 * * root /opt/scripts/refresh_ideal_point.sh#!/usr/bin/env bash # /opt/scripts/refresh_ideal_point.sh set -euo pipefail DATA_DIR/srv/ideal_point ZIP_FILE/data/upload/理想点距离1946-2023年.zip # 校验后解压解压到临时目录再原子替换 unzip -t $ZIP_FILE /dev/null TMP_DIR$(mktemp -d) unzip -q $ZIP_FILE -d $TMP_DIR rm -rf $DATA_DIR mv $TMP_DIR $DATA_DIR echo $(date %Y-%m-%d %H:%M:%S) 数据已更新 /var/log/ideal_point.log逻辑说明脚本先把新上传的 zip 做完整性测试测试通过才解压到临时目录最后用mv做原子替换避免数据库中读到一半的文件。set -euo pipefail是三个保险任何命令失败立刻退出、未定义变量报错、管道中前一个命令失败也报错。参数说明mktemp -d生成系统临时目录rm -rf清空旧数据目录mv是原子操作整个替换过程对外部访问者不可见这点在数据服务化之后很重要。Windows 上做同样的效果用任务计划程序定时跑一个 bat 脚本核心逻辑一样先7z t测试再7z x -y覆盖解压。区别只是没有 Linux 的原子替换语义所以我会解压到一个带版本号的目录如ideal_point_v2024然后更新一个current快捷方式指向它业务系统只读current路径这样更新时不会出现“文件正在被占用”的报错。5. 避坑理想点距离数据从 zip 到分析的 5 个典型翻车现场5.1 解压提示输入密码伪加密拦路现象双击解压“理想点距离1946-2023年.zip”弹出密码框但 README 和数据说明里都没有密码。 原因zip 的加密标志位被置 1数据本身没有加密常见的伪加密手法也可能是发布方打包工具异常写入。 解决用 2.3 节的脚本检查file security status确认无真正加密后把标志位清零重新保存。如果修改后仍要求密码说明数据确被真实加密不应破解去和提供方确认授权或索取密码。5.2 文件名乱码ICU 编码与本地编码打架现象解压出来的 CSV 文件名是“1946.csv”这类数字命名但indicator_definition.xlsx变成了乱码或者 README.txt 打开是乱码。 原因zip 的文件名和文件内容各有一套编码。包内文件内容常见 GBK但文件名用的是 UTF-8在中文版 Windows 上解压时系统按 GBK 解释 UTF-8 编码的文件名部分字符就错位了。 解决Linux 下用unzip -O gbk指定文件名编码部分 unzip 版本支持-O参数Windows 下用 7-Zip 解压并在设置里选“使用 UTF-8 文件名”或直接开启“自动检测”。文件内容乱码则用 3.2 节的编码回退方案处理。5.3 Excel 打开 CSV 后 D 与 D- 列变成“科学计数法”现象用 Excel 打开某个年份的 CSVD 列显示成 1.23E07原本的数值精度丢失对不上。 原因CSV 里的距离数值较大Excel 默认把超过一定位数的数字显示成科学计数法这只影响显示不影响底层数值。可怕的是用户手动另存为 xlsx 后部分精度被真正截断。 解决不要用 Excel 直接编辑原始 CSV分析全程走 Python 或数据工具。如果必须用 Excel 查看导入时选择“数据 → 自文本/CSV”在向导里把 D、D- 列设置为“文本”格式。用 pandas 读入后检查dtype确认是float64而不是object。5.4 missing zip entry中间文件缺失导致整体解压失败现象执行unzip -t时报missing zip entry或file not found指向的是某个早期年份的 CSV。 原因压缩包本身在制作时遗漏了文件条目或者下载过程中文件被截断中央目录与本地文件头不一致。这属于物理损坏不是解压工具的锅。 解决先重新下载一次看问题是否复现。如果复现用zip -FF 理想点距离1946-2023年.zip --out fixed.zip做修复尝试-FF是 unzip 系列的修复模式通过扫描本地文件头重建中央目录。修复后一定要重新跑unzip -t确认所有文件条目完整。如果修复后仍缺特定年份那年的数据只能联系提供方补发。5.5 把 jpg 文件改成 zip 扩展名的伪文件现象拿到一个名为“理想点距离1946-2023年_补充说明.zip”的文件解压工具报“不是有效的压缩文件”。 原因有人为了绕过上传限制把图片或文档直接改扩展名伪装成 zip文件真实格式可能是 jpg、pdf 或 docx。 解决不要双击先看文件头。用xxd 文件.zip | head看前几个字节zip 的起始签名是50 4B 03 04即 PK 两个字符jpg 是FF D8 FFpdf 是25 50 44 46。不是50 4B开头的就不是 zip。真遇到这种情况按真实扩展名改回去再打开。6. 进阶让 1946-2023 年的数据包滚动起来——增量校验小脚本静态数据包的价值有限真正让理想点距离数据变成长期资产的方式是把它接到每年的评价流程里定期补一个年份、自动校验、输出变更报告。我给自己留了一个小脚本每年新数据公布后跑一遍它负责三件事确认新 zip 和已有数据同源、把新年份的数据拼接进长表、给出上一年的排名变动清单。import hashlib import pandas as pd from pathlib import Path NEW_ZIP Path(理想点距离1946-2023年.zip) OLD_PARQUET Path(ideal_point_distance_full.parquet) # 计算 zip 的 SHA256用于记录版本指纹 digest hashlib.sha256(NEW_ZIP.read_bytes()).hexdigest() print(f新包 SHA256: {digest[:16]}...) # 解压并读取新一年的数据复用 3.2 节逻辑省略展开 new_frame pd.read_csv(2024.csv, encodingutf-8-sig) new_frame.insert(0, year, 2024) # 拼接进历史长表 old_frame pd.read_parquet(OLD_PARQUET) merged pd.concat([old_frame, new_frame], ignore_indexTrue) merged.to_parquet(OLD_PARQUET) # 只看最近两年排名变化 pivot merged[merged[year].isin([2023, 2024])] rank_last pivot[pivot[year] 2023].sort_values(C, ascendingFalse)[对象名称].tolist() rank_now pivot[pivot[year] 2024].sort_values(C, ascendingFalse)[对象名称].tolist() changes [(i, o) for i, o in enumerate(rank_now) if rank_last[i] ! o] print(f对比 2023 年2024 年排名变动位置数{len(changes)})逻辑说明脚本用 SHA256 给每次入包的压缩文件做指纹记录在案以后任何人问“今年这份和去年那份是不是同一份数据”比对指纹即可。拼接逻辑和第 3 章一致区别是这里只增量读取新一年文件避免每年全量重读 78 个 CSV。排名变动部分把两年各自按 C 值降序排列逐位比对输出变动位置数量方便快速判断这一年评价结果是否出现大洗牌。参数说明hashlib.sha256返回的是字节摘要hexdigest()转成十六进制字符串read_bytes()一次性读取整个 zip对于大文件会吃内存替代方案是分块读取并逐步更新摘要to_parquet覆盖写入历史表写之前确认旧表已有多余备份列表推导式里的i是排名位次o是该位次上的对象名称只要两年排序在相同位置出现不同对象就计入变动。这个脚本跑完之后我会固定把当年的2024.csv单独留档压缩包本身则按年份归档。因为 zip 里保存的是 1946-2023 年全量数据每年新数据出来时如果发布方给的还是一个全量包我倾向于只取增量文件入库而不是每次用新包覆盖旧包——全量包一旦在传输中出错连带历史数据一起被污染的风险太大这个教训来自一次我直接用新包替换旧表、结果发现新包某年 CSV 损坏、历史数据也跟着回不去的经历。文件都留在原地新数据逐条追加上去zip 只是运输工具分析底座永远是那张校验过的 parquet 长表希望帮到你。本文还有配套的精品资源点击获取