
这是一个基于Streamlit构建的风水命理计算器Web应用。
它结合了中国传统风水理论,包括八宅风水、三元九运、五行生克等,为用户提供个人命理分析和方位建议。
代码结构:
-
导入必要的库
-
设置Streamlit页面配置和中文字体支持
-
定义两个枚举类:Direction(方位)和Element(五行)
-
定义一个FengShuiCalculator类,包含风水命理计算的核心逻辑
-
定义几个可视化函数,用于生成雷达图、柱状图、罗盘图和饼图
-
定义main函数,构建Streamlit应用的界面和交互
下面我们详细解读各个部分。
一、导入的库:
-
streamlit:用于构建Web应用
-
pandas, numpy:数据处理
-
matplotlib, plotly:绘图
-
datetime:获取当前年份
-
enum, typing:定义枚举和类型提示
-
sys, math:系统与数学函数
二、页面配置和中文字体设置
三、枚举类:
-
Direction:表示8个方位和中宫,每个方位有中文名称和角度(用于雷达图)
-
Element:表示五行(金木水火土),每个元素有对应的颜色
四、FengShuiCalculator类:
-
初始化:接收出生年份、性别等参数,并进行验证。
-
计算命卦数(gua_number):根据出生年份和性别,按照一定规则计算。这里区分了1900-1999年和2000-2099年,男女不同。
注意:当计算结果为5时,男性转换为2,女性转换为8。
-
根据命卦数获取五行属性和卦名。
-
根据命卦数获取东西四命分组(东四命:1、3、4、9;西四命:2、6、7、8)。
-
根据命卦数获取八宅风水的方位映射(生气、延年、天医、伏位、祸害、六煞、五鬼、绝命分别对应的方位)。
-
计算特定方位的能量梯度值:结合基础能量(三元九运)、方位修正系数、时间修正系数(流年飞星)和个人命卦修正系数。
-
分析所有方位的吉凶和能量,返回每个方位的详细信息(中文名称、能量值、吉凶类型、吉凶等级、五行属性、颜色、是否吉利)。
-
获取风水建议:包括住宅朝向、卧室方位、办公方位、吉祥颜色和注意事项。
-
其他辅助方法,如获取大运阶段、五行生克等。
五、可视化函数:
-
create_radar_chart:创建雷达图,展示8个方位(不包括中宫)的能量值。
-
create_bar_chart:创建柱状图,展示9个方位的能量值,并按照能量值排序。
-
create_compass_diagram:创建罗盘图,在圆形上标注9个方位的能量值和吉凶。
-
create_pie_chart:创建饼图,展示吉凶等级的分布。
六、Streamlit主函数main:
-
设置页面标题和描述。
-
侧边栏:输入个人信息(出生年份、性别,以及可选的农历月份、日期和时辰)和示例按钮。
-
主内容区:
-
展示基本信息卡片(命卦数、卦名、五行属性、命理分组)
-
四个选项卡:
a. 能量可视化:展示罗盘图、雷达图、柱状图和饼图。
b. 方位分析:以表格形式展示每个方位的详细信息,并解释吉凶方位的含义。
c. 风水建议:展示针对个人的风水建议。
d. 详细信息:展示个人命理信息、当前年份信息和能量计算说明。
-
一、概述
这是一个融合传统风水理论和现代数据可视化的交互式Web应用,主要用于计算个人命理、方位吉凶和风水建议。
二、架构
1. 核心依赖库
# Web框架和数据可视化
import streamlit as st # Web应用框架
import pandas as pd, numpy as np # 数据处理
import matplotlib.pyplot as plt # 静态图表
import plotly.graph_objects as go, plotly.express as px # 交互式图表
2. 核心类设计
(1) Direction枚举类
class Direction(Enum):
"""定义8个方位+中宫"""
# 实现了方位到中文名称、角度的映射
def get_chinese_name(self) -> str # 返回中文方位名
def get_angle(self) -> float # 返回极坐标角度(0-315度)
(2) Element枚举类
class Element(Enum):
"""五行(金木水火土)"""
def get_color(self) -> str # 返回五行对应的颜色
(3) FengShuiCalculator主计算类
这是应用的核心逻辑类,实现了完整的命理计算。
三、算法解析
1. 命卦计算算法 (get_gua_number)
# 基于洛书九宫和八宅风水理论
# 1900-1999年:男性 (100-年份末两位)%9
# 1900-1999年:女性 (年份末两位-4)%9
# 2000-2099年:男性 (99-年份末两位)%9
# 2000-2099年:女性 (年份末两位+6)%9
# 特殊处理:结果为5时,男→2(坤),女→8(艮)
2. 方位吉凶映射 (get_direction_mapping)
每个命卦对应8个方位,各有不同吉凶属性:
-
吉方:生气(大吉)、延年(中吉)、天医(中吉)、伏位(小吉)
-
凶方:祸害(小凶)、六煞(次凶)、五鬼(大凶)、绝命(大凶)
3. 能量梯度计算模型
energy = base_energy * direction_coefficient * time_coefficient * personal_coefficient
(1) 基础能量 (_get_base_energy)
基于三元九运理论,每20年一运:
- 一运(1864-1883): 0.8, 二运: 0.6, ..., 九运(2024-2043): 1.0
(2) 方位系数 (_get_direction_coefficient)
各方位固有能量系数:
- 南(火): 1.1, 东(木): 1.0, 西南(土): 0.8, 等
(3) 时间系数 (_get_time_coefficient)
基于流年飞星理论,每年不同:
- 2024(八白左辅星): 1.0, 2025(九紫右弼星): 1.1, 等
(4) 个人系数 (_get_personal_coefficient)
基于个人命卦与方位关系:
- 生气方: 1.5, 延年: 1.3, ..., 绝命: 0.5
四、可视化
1. 罗盘图 (create_compass_diagram)
-
使用matplotlib绘制
-
圆形布局显示8个方位+中宫
-
颜色编码表示吉凶等级
2. 雷达图 (create_radar_chart)
-
极坐标显示各方位能量分布
-
多边形填充增强视觉
3. 柱状图 (create_bar_chart)
-
使用Plotly交互式图表
-
按能量值排序显示
-
悬停显示详细信息
4. 饼图 (create_pie_chart)
-
显示吉凶等级分布比例
-
环形图设计
五、Streamlit界面
1. 页面布局
st.set_page_config(
page_title="风水命理计算器",
page_icon="🏮",
layout="wide", # 宽屏布局
initial_sidebar_state="expanded"
)
2. 侧边栏设计
-
用户输入:出生年份、性别
-
高级选项:农历月日、时辰
-
示例按钮:快速填充测试数据
-
关于说明:应用背景介绍
3. 主界面结构
采用选项卡式布局:
-
📈 能量可视化:图表展示
-
🧭 方位分析:详细数据表格
-
💡 风水建议:个性化建议
-
📋 详细信息:命理解读
六、数据处理
用户输入 → 验证 → 命卦计算 → 五行属性 → 方位映射
↓
能量计算 → 吉凶分析 → 可视化 → 建议生成
↓
界面展示
七、特性
1. 算法特点
-
多重系数综合:基础+方位+时间+个人
-
动态调整:年份变化影响计算结果
-
传统理论结合:八宅风水+三元九运+流年飞星
2. 用户体验
-
实时计算:输入即计算
-
多重可视化:4种图表从不同角度展示
-
详细解释:每个结果都有理论依据
-
响应式设计:适应不同屏幕
3. 代码质量
-
类型提示:完整的类型注解
-
错误处理:输入验证和异常捕获
-
模块化设计:高内聚低耦合
-
配置分离:常量集中管理
八、完整代码
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import plotly.express as px
from datetime import datetime
from enum import Enum
from typing import Dict, List, Tuple, Optional
import sys
import math
# 设置页面配置
st.set_page_config(
page_title="风水命理计算器",
page_icon="🏮",
layout="wide",
initial_sidebar_state="expanded"
)
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
class Direction(Enum):
"""方位枚举"""
NORTH = 0
NORTHEAST = 1
EAST = 2
SOUTHEAST = 3
SOUTH = 4
SOUTHWEST = 5
WEST = 6
NORTHWEST = 7
CENTER = 8
def get_chinese_name(self) -> str:
"""获取中文方位名称"""
names = {
Direction.NORTH: "北",
Direction.NORTHEAST: "东北",
Direction.EAST: "东",
Direction.SOUTHEAST: "东南",
Direction.SOUTH: "南",
Direction.SOUTHWEST: "西南",
Direction.WEST: "西",
Direction.NORTHWEST: "西北",
Direction.CENTER: "中"
}
return names[self]
def get_angle(self) -> float:
"""获取方位的角度(用于雷达图)"""
angles = {
Direction.NORTH: 0,
Direction.NORTHEAST: 45,
Direction.EAST: 90,
Direction.SOUTHEAST: 135,
Direction.SOUTH: 180,
Direction.SOUTHWEST: 225,
Direction.WEST: 270,
Direction.NORTHWEST: 315,
Direction.CENTER: 0
}
return angles[self]
class Element(Enum):
"""五行枚举"""
METAL = "金"
WOOD = "木"
WATER = "水"
FIRE = "火"
EARTH = "土"
def get_color(self) -> str:
"""获取五行对应的颜色"""
colors = {
"金": "#FFD700", # 金色
"木": "#32CD32", # 绿色
"水": "#1E90FF", # 蓝色
"火": "#FF4500", # 红色
"土": "#DAA520" # 土色
}
return colors[self.value]
class FengShuiCalculator:
"""风水命理计算器"""
def __init__(self, birth_year: int, gender: bool, lunar_month: Optional[int] = None,
lunar_day: Optional[int] = None, hour: Optional[int] = None):
"""
初始化风水计算器
Args:
birth_year: 出生年份(公历)
gender: 性别,True为男性,False为女性
lunar_month: 农历月份(可选)
lunar_day: 农历日期(可选)
hour: 出生时辰(0-23,可选)
"""
self.birth_year = birth_year
self.gender = gender
self.lunar_month = lunar_month
self.lunar_day = lunar_day
self.hour = hour
self.current_year = datetime.now().year
# 验证输入
self._validate_inputs()
def _validate_inputs(self) -> None:
"""验证输入参数"""
if not (1900 <= self.birth_year <= 2099):
raise ValueError("出生年份需在1900-2099年之间")
if self.lunar_month and not (1 <= self.lunar_month <= 12):
raise ValueError("农历月份需在1-12之间")
if self.lunar_day and not (1 <= self.lunar_day <= 30):
raise ValueError("农历日期需在1-30之间")
if self.hour is not None and not (0 <= self.hour <= 23):
raise ValueError("时辰需在0-23之间")
def get_gua_number(self) -> int:
"""
计算命卦数(洛书九宫)
Returns:
int: 命卦数(1-9,不包括5)
"""
# 取年份后两位
last_two_digits = self.birth_year % 100
if 1900 <= self.birth_year <= 1999:
if self.gender: # 1900-1999年男性
gua_num = (100 - last_two_digits) % 9
else: # 1900-1999年女性
gua_num = (last_two_digits - 4) % 9
else: # 2000-2099年
if self.gender: # 2000-2099年男性
gua_num = (99 - last_two_digits) % 9
else: # 2000-2099年女性
gua_num = (last_two_digits + 6) % 9
# 处理结果为0的情况(应为9)
if gua_num == 0:
gua_num = 9
# 处理结果为5的情况(五黄居中)
# 根据传统风水理论:男命5视为2(坤),女命5视为8(艮)
if gua_num == 5:
if self.gender: # 男性
gua_num = 2
else: # 女性
gua_num = 8
return gua_num
def get_element(self, gua_number: Optional[int] = None) -> Tuple[Element, str]:
"""
获取五行属性和卦名
Args:
gua_number: 命卦数,如为None则使用当前对象的命卦
Returns:
Tuple[Element, str]: (五行属性, 卦名)
"""
if gua_number is None:
gua_number = self.get_gua_number()
gua_info = {
1: (Element.WATER, "坎卦"),
2: (Element.EARTH, "坤卦"),
3: (Element.WOOD, "震卦"),
4: (Element.WOOD, "巽卦"),
6: (Element.METAL, "乾卦"),
7: (Element.METAL, "兑卦"),
8: (Element.EARTH, "艮卦"),
9: (Element.FIRE, "离卦")
}
if gua_number not in gua_info:
raise ValueError(f"无效的命卦数: {gua_number}")
return gua_info[gua_number]
def get_life_group(self, gua_number: Optional[int] = None) -> str:
"""
获取东西四命分组
Returns:
str: "东四命" 或 "西四命"
"""
if gua_number is None:
gua_number = self.get_gua_number()
east_group = {1, 3, 4, 9} # 东四命:坎、震、巽、离
west_group = {2, 6, 7, 8} # 西四命:坤、乾、兑、艮
if gua_number in east_group:
return "东四命"
elif gua_number in west_group:
return "西四命"
else:
raise ValueError(f"无效的命卦数: {gua_number}")
def get_direction_mapping(self, gua_number: Optional[int] = None) -> Dict[str, Direction]:
"""
根据命卦获取方位映射(八宅风水)
Args:
gua_number: 命卦数,如为None则使用当前对象的命卦
Returns:
Dict[str, Direction]: 各吉凶方位映射
"""
if gua_number is None:
gua_number = self.get_gua_number()
# 八宅风水方位配置
direction_maps = {
# 坎命(1)
1: {
"生气": Direction.EAST, # 大吉
"延年": Direction.SOUTHEAST, # 中吉
"天医": Direction.SOUTH, # 中吉
"伏位": Direction.NORTH, # 小吉
"祸害": Direction.SOUTHWEST, # 小凶
"六煞": Direction.WEST, # 次凶
"五鬼": Direction.NORTHEAST, # 大凶
"绝命": Direction.NORTHWEST # 大凶
},
# 坤命(2)
2: {
"生气": Direction.WEST,
"延年": Direction.NORTHEAST,
"天医": Direction.NORTHWEST,
"伏位": Direction.SOUTH,
"祸害": Direction.NORTH,
"六煞": Direction.EAST,
"五鬼": Direction.SOUTHEAST,
"绝命": Direction.SOUTHWEST
},
# 震命(3)
3: {
"生气": Direction.SOUTHEAST,
"延年": Direction.EAST,
"天医": Direction.SOUTH,
"伏位": Direction.NORTH,
"祸害": Direction.SOUTHWEST,
"六煞": Direction.WEST,
"五鬼": Direction.NORTHWEST,
"绝命": Direction.NORTHEAST
},
# 巽命(4)
4: {
"生气": Direction.NORTHWEST,
"延年": Direction.WEST,
"天医": Direction.NORTHEAST,
"伏位": Direction.SOUTH,
"祸害": Direction.SOUTHWEST,
"六煞": Direction.EAST,
"五鬼": Direction.SOUTHEAST,
"绝命": Direction.NORTH
},
# 乾命(6)
6: {
"生气": Direction.NORTHWEST,
"延年": Direction.WEST,
"天医": Direction.NORTHEAST,
"伏位": Direction.SOUTH,
"祸害": Direction.SOUTHWEST,
"六煞": Direction.EAST,
"五鬼": Direction.SOUTHEAST,
"绝命": Direction.NORTH
},
# 兑命(7)
7: {
"生气": Direction.SOUTHWEST,
"延年": Direction.NORTH,
"天医": Direction.NORTHEAST,
"伏位": Direction.EAST,
"祸害": Direction.SOUTHEAST,
"六煞": Direction.SOUTH,
"五鬼": Direction.WEST,
"绝命": Direction.NORTHWEST
},
# 艮命(8)
8: {
"生气": Direction.SOUTHWEST,
"延年": Direction.NORTH,
"天医": Direction.NORTHEAST,
"伏位": Direction.EAST,
"祸害": Direction.SOUTHEAST,
"六煞": Direction.SOUTH,
"五鬼": Direction.WEST,
"绝命": Direction.NORTHWEST
},
# 离命(9)
9: {
"生气": Direction.SOUTH,
"延年": Direction.EAST,
"天医": Direction.SOUTHEAST,
"伏位": Direction.NORTH,
"祸害": Direction.WEST,
"六煞": Direction.NORTHEAST,
"五鬼": Direction.NORTHWEST,
"绝命": Direction.SOUTHWEST
}
}
if gua_number not in direction_maps:
raise ValueError(f"不支持的命卦数: {gua_number}")
return direction_maps[gua_number]
def calculate_energy_gradient(self, direction: Direction, year: Optional[int] = None) -> float:
"""
计算特定方位的能量梯度值
Args:
direction: 方位
year: 年份,如为None则使用当前年份
Returns:
float: 能量梯度值
"""
if year is None:
year = self.current_year
# 基础能量值(三元九运理论)
base_energy = self._get_base_energy(year)
# 方位修正系数
direction_coefficient = self._get_direction_coefficient(direction)
# 时间修正系数
time_coefficient = self._get_time_coefficient(year)
# 个人命卦修正
personal_coefficient = self._get_personal_coefficient(direction)
# 综合能量梯度
energy_gradient = base_energy * direction_coefficient * time_coefficient * personal_coefficient
return energy_gradient
def _get_base_energy(self, year: int) -> float:
"""
根据年份获取基础能量值(三元九运)
Args:
year: 年份
Returns:
float: 基础能量值
"""
# 三元九运周期计算(每20年一运)
cycle_start = 1864 # 一运开始年份
period = ((year - cycle_start) // 20) % 9 + 1
# 九运能量配置
period_energy = {
1: 0.8, # 一运 (1864-1883)
2: 0.6, # 二运 (1884-1903)
3: 0.4, # 三运 (1904-1923)
4: 0.2, # 四运 (1924-1943)
5: 0.3, # 五运 (1944-1963)
6: 0.4, # 六运 (1964-1983)
7: 0.6, # 七运 (1984-2003)
8: 0.8, # 八运 (2004-2023)
9: 1.0 # 九运 (2024-2043)
}
return period_energy.get(period, 0.5)
def _get_direction_coefficient(self, direction: Direction) -> float:
"""
获取方位修正系数
Args:
direction: 方位
Returns:
float: 方位修正系数
"""
coefficients = {
Direction.EAST: 1.0, # 东:木,春季旺
Direction.SOUTHEAST: 0.9, # 东南:木,春夏之交
Direction.SOUTH: 1.1, # 南:火,夏季旺
Direction.SOUTHWEST: 0.8, # 西南:土,夏秋之交
Direction.WEST: 0.7, # 西:金,秋季旺
Direction.NORTHWEST: 0.6, # 西北:金,秋冬之交
Direction.NORTH: 0.8, # 北:水,冬季旺
Direction.NORTHEAST: 0.7, # 东北:土,冬春之交
Direction.CENTER: 1.0 # 中:土
}
return coefficients.get(direction, 1.0)
def _get_time_coefficient(self, year: int) -> float:
"""
获取时间修正系数(流年飞星)
Args:
year: 年份
Returns:
float: 时间修正系数
"""
# 流年飞星影响
flying_stars = {
2024: 1.0, # 八白左辅星
2025: 1.1, # 九紫右弼星
2026: 1.0, # 一白贪狼星
2027: 0.9, # 二黑巨门星
2028: 0.8, # 三碧禄存星
2029: 1.0, # 四绿文曲星
2030: 1.1, # 五黄廉贞星
2031: 1.0, # 六白武曲星
2032: 0.9, # 七赤破军星
2033: 0.8 # 八白左辅星
}
if year in flying_stars:
return flying_stars[year]
# 近年趋势
if year > 2025:
return 1.0 + (year - 2025) * 0.01
else:
return 1.0 - (2025 - year) * 0.01
def _get_personal_coefficient(self, direction: Direction) -> float:
"""
获取个人命卦修正系数
Args:
direction: 方位
Returns:
float: 个人修正系数
"""
gua_number = self.get_gua_number()
direction_map = self.get_direction_mapping(gua_number)
# 根据方位吉凶确定系数
for luck_type, dir_obj in direction_map.items():
if direction == dir_obj:
if luck_type == "生气":
return 1.5
elif luck_type == "延年":
return 1.3
elif luck_type == "天医":
return 1.2
elif luck_type == "伏位":
return 1.0
elif luck_type == "祸害":
return 0.8
elif luck_type == "六煞":
return 0.7
elif luck_type == "五鬼":
return 0.6
elif luck_type == "绝命":
return 0.5
return 1.0
def get_life_stage(self, year: Optional[int] = None) -> str:
"""
获取大运阶段
Args:
year: 年份,如为None则使用当前年份
Returns:
str: 大运阶段描述
"""
if year is None:
year = self.current_year
age = year - self.birth_year
if age <= 10:
return "童年运 (1-10岁)"
elif age <= 20:
return "少年运 (11-20岁)"
elif age <= 40:
return "中年运 (21-40岁)"
elif age <= 60:
return "壮年运 (41-60岁)"
else:
return "晚年运 (61岁以上)"
def analyze_directions(self) -> Dict[str, Dict]:
"""
分析所有方位的吉凶和能量
Returns:
Dict[str, Dict]: 各方位分析结果
"""
gua_number = self.get_gua_number()
direction_map = self.get_direction_mapping(gua_number)
analysis_result = {}
for direction in Direction:
# 计算能量值
energy = self.calculate_energy_gradient(direction)
# 确定吉凶类型
luck_type = None
for type_name, dir_obj in direction_map.items():
if direction == dir_obj:
luck_type = type_name
break
# 判断吉凶等级
if luck_type in ["生气", "延年", "天医", "伏位"]:
if energy > 1.2:
level = "大吉"
elif energy > 0.9:
level = "中吉"
else:
level = "小吉"
elif luck_type in ["祸害", "六煞"]:
if energy > 0.8:
level = "小凶"
else:
level = "中凶"
elif luck_type in ["五鬼", "绝命"]:
if energy > 0.6:
level = "大凶"
else:
level = "次凶"
else:
if energy > 0.8:
level = "平"
elif energy > 0.6:
level = "小凶"
else:
level = "凶"
# 五行属性
direction_elements = {
Direction.NORTH: "水",
Direction.NORTHEAST: "土",
Direction.EAST: "木",
Direction.SOUTHEAST: "木",
Direction.SOUTH: "火",
Direction.SOUTHWEST: "土",
Direction.WEST: "金",
Direction.NORTHWEST: "金",
Direction.CENTER: "土"
}
# 吉凶颜色
level_colors = {
"大吉": "#4CAF50", # 绿色
"中吉": "#8BC34A", # 浅绿色
"小吉": "#CDDC39", # 黄绿色
"平": "#FFC107", # 黄色
"小凶": "#FF9800", # 橙色
"中凶": "#FF5722", # 深橙色
"大凶": "#F44336", # 红色
"次凶": "#E91E63", # 粉红色
"凶": "#9C27B0" # 紫色
}
analysis_result[direction.name] = {
"chinese_name": direction.get_chinese_name(),
"energy": round(energy, 3),
"luck_type": luck_type or "无",
"level": level,
"element": direction_elements.get(direction, "未知"),
"color": level_colors.get(level, "#9E9E9E"),
"favorable": luck_type in ["生气", "延年", "天医", "伏位"] if luck_type else False
}
return analysis_result
def get_recommendations(self) -> Dict[str, List[str]]:
"""
获取风水建议
Returns:
Dict[str, List[str]]: 各类建议
"""
gua_number = self.get_gua_number()
element, gua_name = self.get_element(gua_number)
life_group = self.get_life_group(gua_number)
recommendations = {
"住宅朝向": [],
"卧室方位": [],
"办公方位": [],
"吉祥颜色": [],
"注意事项": []
}
# 获取吉方
direction_map = self.get_direction_mapping(gua_number)
lucky_directions = {k: v for k, v in direction_map.items()
if k in ["生气", "延年", "天医", "伏位"]}
# 住宅朝向建议
if "生气" in lucky_directions:
rec = f"最佳朝向:{lucky_directions['生气'].get_chinese_name()}方(生气方,大吉)"
recommendations["住宅朝向"].append(rec)
# 卧室方位建议
for luck_type, direction in lucky_directions.items():
if luck_type in ["延年", "天医"]:
rec = f"卧室宜在{direction.get_chinese_name()}方({luck_type}方)"
recommendations["卧室方位"].append(rec)
# 办公方位建议
if "伏位" in lucky_directions:
rec = f"办公桌宜面向{lucky_directions['伏位'].get_chinese_name()}方(伏位方,稳定)"
recommendations["办公方位"].append(rec)
# 吉祥颜色
color_map = {
Element.METAL: ["白色", "金色", "银色"],
Element.WOOD: ["绿色", "青色"],
Element.WATER: ["黑色", "蓝色", "灰色"],
Element.FIRE: ["红色", "紫色", "粉色"],
Element.EARTH: ["黄色", "棕色", "米色"]
}
recommendations["吉祥颜色"] = color_map.get(element, [])
# 注意事项
recommendations["注意事项"].append(f"您是{life_group},适合与{life_group}的人合作")
recommendations["注意事项"].append(f"您的五行属{element.value},宜补{self._get_element_to_support(element)}")
return recommendations
def _get_element_to_support(self, element: Element) -> str:
"""获取需要补充的五行"""
support_map = {
"金": "土", # 土生金
"木": "水", # 水生木
"水": "金", # 金生水
"火": "木", # 木生火
"土": "火" # 火生土
}
return support_map.get(element.value, element.value)
def _get_period_name(self, year: int) -> str:
"""获取三元九运名称"""
cycle_start = 1864
period = ((year - cycle_start) // 20) % 9 + 1
period_names = {
1: "一运 (1864-1883)",
2: "二运 (1884-1903)",
3: "三运 (1904-1923)",
4: "四运 (1924-1943)",
5: "五运 (1944-1963)",
6: "六运 (1964-1983)",
7: "七运 (1984-2003)",
8: "八运 (2004-2023)",
9: "九运 (2024-2043)"
}
return period_names.get(period, "未知")
# 创建可视化图表
def create_radar_chart(analysis_result):
"""创建雷达图展示方位能量"""
directions = []
energies = []
colors = []
for direction in Direction:
if direction != Direction.CENTER: # 中宫不显示在雷达图中
info = analysis_result[direction.name]
directions.append(info['chinese_name'])
energies.append(info['energy'])
colors.append(info['color'])
# 确保数据是闭合的(雷达图需要首尾相连)
directions.append(directions[0])
energies.append(energies[0])
# 转换为极坐标
angles = np.linspace(0, 2 * np.pi, len(directions), endpoint=True)
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(projection='polar'))
# 绘制雷达图
ax.plot(angles, energies, 'o-', linewidth=2)
ax.fill(angles, energies, alpha=0.25)
# 设置标签
ax.set_xticks(angles[:-1])
ax.set_xticklabels(directions[:-1], fontsize=12)
# 设置y轴范围
max_energy = max(energies) if energies else 1.5
ax.set_ylim(0, max_energy * 1.2)
# 设置网格
ax.grid(True)
# 设置标题
ax.set_title("方位能量雷达图", fontsize=16, fontweight='bold', va='bottom')
return fig
def create_bar_chart(analysis_result):
"""创建柱状图展示方位能量"""
directions = []
energies = []
colors = []
luck_types = []
for direction in Direction:
info = analysis_result[direction.name]
directions.append(info['chinese_name'])
energies.append(info['energy'])
colors.append(info['color'])
luck_types.append(info['luck_type'])
# 创建DataFrame
df = pd.DataFrame({
'方位': directions,
'能量值': energies,
'吉凶类型': luck_types,
'颜色': colors
})
# 排序
df = df.sort_values('能量值', ascending=False)
# 创建柱状图
fig = go.Figure(data=[
go.Bar(
x=df['方位'],
y=df['能量值'],
marker_color=df['颜色'],
text=df['吉凶类型'],
textposition='auto',
hovertext=df.apply(
lambda row: f"方位: {row['方位']}<br>能量: {row['能量值']:.3f}<br>吉凶: {row['吉凶类型']}", axis=1),
hoverinfo="text"
)
])
fig.update_layout(
title="方位能量柱状图",
xaxis_title="方位",
yaxis_title="能量值",
template="plotly_white",
height=500
)
return fig
def create_compass_diagram(analysis_result):
"""创建罗盘图"""
fig, ax = plt.subplots(figsize=(8, 8))
# 设置圆形
circle = plt.Circle((0, 0), 1, color='lightgray', alpha=0.3)
ax.add_patch(circle)
# 设置坐标轴
ax.set_xlim(-1.2, 1.2)
ax.set_ylim(-1.2, 1.2)
ax.set_aspect('equal')
ax.axis('off')
# 绘制方位和能量
for direction in Direction:
if direction != Direction.CENTER:
info = analysis_result[direction.name]
# 计算位置
angle_rad = np.deg2rad(direction.get_angle())
x = np.cos(angle_rad) * 0.8
y = np.sin(angle_rad) * 0.8
# 绘制点
ax.plot(x, y, 'o', markersize=20, color=info['color'], alpha=0.7)
# 添加文本
ax.text(x, y, f"{info['chinese_name']}\n{info['energy']:.2f}",
ha='center', va='center', fontsize=10, fontweight='bold')
# 添加中心点
center_info = analysis_result[Direction.CENTER.name]
ax.plot(0, 0, 'o', markersize=30, color=center_info['color'], alpha=0.7)
ax.text(0, 0, f"中\n{center_info['energy']:.2f}",
ha='center', va='center', fontsize=12, fontweight='bold')
# 添加标题
ax.set_title("风水罗盘图", fontsize=16, fontweight='bold', pad=20)
return fig
def create_pie_chart(analysis_result):
"""创建饼图展示吉凶分布"""
level_counts = {
"大吉": 0,
"中吉": 0,
"小吉": 0,
"平": 0,
"小凶": 0,
"中凶": 0,
"大凶": 0,
"次凶": 0,
"凶": 0
}
level_colors = {
"大吉": "#4CAF50",
"中吉": "#8BC34A",
"小吉": "#CDDC39",
"平": "#FFC107",
"小凶": "#FF9800",
"中凶": "#FF5722",
"大凶": "#F44336",
"次凶": "#E91E63",
"凶": "#9C27B0"
}
for direction in Direction:
info = analysis_result[direction.name]
if info['level'] in level_counts:
level_counts[info['level']] += 1
# 过滤掉数量为0的类别
labels = []
sizes = []
colors = []
for level, count in level_counts.items():
if count > 0:
labels.append(level)
sizes.append(count)
colors.append(level_colors.get(level, "#9E9E9E"))
# 创建饼图
fig = go.Figure(data=[go.Pie(
labels=labels,
values=sizes,
marker_colors=colors,
hole=0.3,
textinfo='label+percent',
hoverinfo='label+value+percent'
)])
fig.update_layout(
title="吉凶分布饼图",
height=500
)
return fig
# Streamlit应用主函数
def main():
# 应用标题
st.title("🏮 风水命理计算器")
st.markdown("基于传统风水理论,提供个人命理分析和方位建议")
# 侧边栏
with st.sidebar:
st.header("🔮 个人信息输入")
# 输入出生年份
birth_year = st.number_input(
"出生年份",
min_value=1900,
max_value=2099,
value=1990,
step=1,
help="请输入1900-2099年之间的出生年份"
)
# 输入性别
gender_input = st.radio("性别", ["男", "女"], index=0)
gender = gender_input == "男"
# 高级选项
with st.expander("高级选项(可选)"):
lunar_month = st.number_input("农历月份", min_value=1, max_value=12, value=None, step=1)
lunar_day = st.number_input("农历日期", min_value=1, max_value=30, value=None, step=1)
hour = st.number_input("出生时辰(0-23)", min_value=0, max_value=23, value=None, step=1)
# 计算按钮
calculate_btn = st.button("开始计算", type="primary", use_container_width=True)
# 示例
st.markdown("---")
st.markdown("### 📊 示例数据")
if st.button("1985年男性示例"):
st.session_state.birth_year = 1985
st.session_state.gender = True
st.rerun()
if st.button("1990年女性示例"):
st.session_state.birth_year = 1990
st.session_state.gender = False
st.rerun()
# 关于
st.markdown("---")
st.markdown("### ℹ️ 关于")
st.markdown("""
本计算器基于传统风水理论,包括:
- 八宅风水
- 三元九运
- 五行生克
- 流年飞星
结果仅供参考,不作为实际决策依据。
""")
# 主内容区域
if 'birth_year' in st.session_state:
birth_year = st.session_state.birth_year
if 'gender' in st.session_state:
gender = st.session_state.gender
try:
# 创建计算器实例
calculator = FengShuiCalculator(birth_year, gender)
# 基本信息卡片
col1, col2, col3, col4 = st.columns(4)
with col1:
gua_number = calculator.get_gua_number()
st.metric("命卦数", gua_number)
with col2:
element, gua_name = calculator.get_element(gua_number)
st.metric("卦名", gua_name)
with col3:
element_display = element.value
st.metric("五行属性", element_display)
with col4:
life_group = calculator.get_life_group(gua_number)
st.metric("命理分组", life_group)
# 分隔线
st.markdown("---")
# 创建选项卡
tab1, tab2, tab3, tab4 = st.tabs(["📈 能量可视化", "🧭 方位分析", "💡 风水建议", "📋 详细信息"])
with tab1:
st.subheader("方位能量可视化")
# 获取分析结果
analysis_result = calculator.analyze_directions()
# 创建两列布局
col1, col2 = st.columns(2)
with col1:
st.subheader("罗盘图")
compass_fig = create_compass_diagram(analysis_result)
st.pyplot(compass_fig)
with col2:
st.subheader("雷达图")
radar_fig = create_radar_chart(analysis_result)
st.pyplot(radar_fig)
# 柱状图
st.subheader("能量柱状图")
bar_fig = create_bar_chart(analysis_result)
st.plotly_chart(bar_fig, use_container_width=True)
# 饼图
col1, col2 = st.columns([2, 1])
with col1:
st.subheader("吉凶分布")
pie_fig = create_pie_chart(analysis_result)
st.plotly_chart(pie_fig, use_container_width=True)
with col2:
st.subheader("图例说明")
st.markdown("""
- 🟢 **大吉**:能量 > 1.2
- 🟡 **中吉**:能量 0.9-1.2
- 🟡 **小吉**:能量 < 0.9
- 🟡 **平**:能量 0.8-1.0
- 🟠 **小凶**:能量 0.6-0.8
- 🔴 **中凶**:能量 < 0.6
- 🔴 **大凶**:能量 < 0.6
""")
with tab2:
st.subheader("方位详细分析")
# 创建DataFrame显示数据
data = []
for direction in Direction:
info = analysis_result[direction.name]
data.append({
"方位": info['chinese_name'],
"能量值": info['energy'],
"吉凶类型": info['luck_type'],
"吉凶等级": info['level'],
"五行": info['element'],
"是否吉利": "是" if info['favorable'] else "否"
})
df = pd.DataFrame(data)
# 添加颜色映射
def color_row(row):
colors = {
"大吉": "background-color: #4CAF50; color: white",
"中吉": "background-color: #8BC34A; color: white",
"小吉": "background-color: #CDDC39; color: black",
"平": "background-color: #FFC107; color: black",
"小凶": "background-color: #FF9800; color: white",
"中凶": "background-color: #FF5722; color: white",
"大凶": "background-color: #F44336; color: white",
"次凶": "background-color: #E91E63; color: white",
"凶": "background-color: #9C27B0; color: white"
}
return [colors.get(row['吉凶等级'], '')] * len(row)
# 显示表格
st.dataframe(df.style.apply(color_row, axis=1), use_container_width=True)
# 吉凶方位说明
st.subheader("吉凶方位说明")
col1, col2 = st.columns(2)
with col1:
st.markdown("### 吉方")
st.markdown("""
- **生气**:大吉,主事业财运
- **延年**:中吉,主健康长寿
- **天医**:中吉,主健康贵人
- **伏位**:小吉,主平安稳定
""")
with col2:
st.markdown("### 凶方")
st.markdown("""
- **祸害**:小凶,主口舌是非
- **六煞**:次凶,主烦恼不顺
- **五鬼**:大凶,主灾病意外
- **绝命**:大凶,主破财伤身
""")
with tab3:
st.subheader("风水建议")
recommendations = calculator.get_recommendations()
col1, col2 = st.columns(2)
with col1:
st.markdown("### 🏠 住宅与卧室")
for rec in recommendations["住宅朝向"]:
st.info(rec)
for rec in recommendations["卧室方位"]:
st.success(rec)
with col2:
st.markdown("### 💼 办公与颜色")
for rec in recommendations["办公方位"]:
st.warning(rec)
st.markdown("#### 吉祥颜色")
colors_html = ""
for color in recommendations["吉祥颜色"]:
colors_html += f'<span style="display:inline-block; padding:5px 15px; margin:5px; background-color:lightgray; border-radius:5px;">{color}</span>'
st.markdown(colors_html, unsafe_allow_html=True)
st.markdown("### 📋 注意事项")
for rec in recommendations["注意事项"]:
st.markdown(f"- {rec}")
with tab4:
st.subheader("详细信息")
col1, col2 = st.columns(2)
with col1:
st.markdown("### 个人命理信息")
st.markdown(f"**出生年份**:{birth_year}年")
st.markdown(f"**性别**:{'男' if gender else '女'}")
st.markdown(f"**命卦**:{gua_number} ({gua_name})")
st.markdown(f"**五行属性**:{element.value}")
st.markdown(f"**命理分组**:{life_group}")
# 大运阶段
life_stage = calculator.get_life_stage()
st.markdown(f"**当前大运**:{life_stage}")
# 五行生克关系
st.markdown("### 五行生克关系")
element_obj, _ = calculator.get_element()
st.markdown(f"**您的五行**:{element_obj.value}")
st.markdown(f"**宜补充的五行**:{calculator._get_element_to_support(element_obj)}")
with col2:
st.markdown("### 当前年份信息")
current_year = datetime.now().year
st.markdown(f"**当前年份**:{current_year}年")
st.markdown(f"**三元九运**:{calculator._get_period_name(current_year)}")
st.markdown(f"**流年飞星系数**:{calculator._get_time_coefficient(current_year):.2f}")
# 能量计算说明
st.markdown("### 能量计算说明")
st.markdown("""
能量值 = 基础能量 × 方位系数 × 时间系数 × 个人系数
- **基础能量**:基于三元九运理论
- **方位系数**:各方位固有属性
- **时间系数**:流年飞星影响
- **个人系数**:个人命卦修正
""")
except ValueError as e:
st.error(f"输入错误:{e}")
except Exception as e:
st.error(f"计算错误:{e}")
if __name__ == "__main__":
main()