-
-
Save OttoWinter/58ce0fd4c7743d233af8a10ed7ff940a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from collections import namedtuple, Counter\n", | |
"import itertools" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 77, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"State = namedtuple('State', ['state', 'attributes'])\n", | |
"\n", | |
"state1 = State('ON', {\n", | |
" 'brightness': 255,\n", | |
" 'xy_color': [1.0, 1.0],\n", | |
" 'rgb_color': [255, 255, 255],\n", | |
" 'white_value': 0,\n", | |
" 'color_temp': 42,\n", | |
" 'min_mireds': 154,\n", | |
" 'max_mireds': 500,\n", | |
" 'effect_list': ['None', 'Random'],\n", | |
" 'effect': 'None',\n", | |
" 'supported_features': 255\n", | |
"})\n", | |
"state2 = State('OFF', {\n", | |
" 'brightness': 255,\n", | |
" 'xy_color': [1.0, 1.0],\n", | |
" 'rgb_color': [255, 255, 255],\n", | |
" 'white_value': 0,\n", | |
" 'color_temp': 42,\n", | |
" 'min_mireds': 154,\n", | |
" 'max_mireds': 500,\n", | |
" 'effect_list': ['None', 'Random'],\n", | |
" 'effect': 'None',\n", | |
" 'supported_features': 255\n", | |
"})\n", | |
"state3 = State('ON', {\n", | |
" 'brightness': 255,\n", | |
" 'xy_color': [1.0, 1.0],\n", | |
" 'rgb_color': [255, 255, 255],\n", | |
" 'white_value': 0,\n", | |
" 'effect_list': ['None', 'Random'],\n", | |
" 'effect': 'None',\n", | |
" 'supported_features': 255 - 2\n", | |
"})\n", | |
"state4 = State('ON', {\n", | |
" 'brightness': 255,\n", | |
" 'xy_color': [1.0, 1.0],\n", | |
" 'rgb_color': [255, 255, 255],\n", | |
" 'white_value': 0,\n", | |
" 'supported_features': 255 - (2 + 4)\n", | |
"})\n", | |
"state5 = State('OFF', {\n", | |
" 'brightness': 255,\n", | |
" 'xy_color': [1.0, 1.0],\n", | |
" 'rgb_color': [255, 255, 255],\n", | |
" 'white_value': 0,\n", | |
" 'supported_features': 255 - (2 + 4)\n", | |
"})\n", | |
"state6 = State('OFF', {\n", | |
" 'brightness': 255,\n", | |
" 'rgb_color': None,\n", | |
" 'white_value': None,\n", | |
" 'supported_features': 1\n", | |
"})\n", | |
"state7 = State('ON', {\n", | |
" 'brightness': 255,\n", | |
" 'supported_features': 1\n", | |
"})\n", | |
"\n", | |
"states = []\n", | |
"for state in (state1, state2, state3, state4, state5, state6, state7):\n", | |
" for i in range(5):\n", | |
" states.append(state)\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 78, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def f(states):\n", | |
" on_states = [state for state in states if state.state == 'ON']\n", | |
"\n", | |
" state = _determine_on_off_state(states)\n", | |
"\n", | |
" brightness = _reduce_attribute(on_states, 'brightness')\n", | |
" xy_color = _reduce_attribute(\n", | |
" on_states, 'xy_color', reduce=_average_tuple)\n", | |
" rgb_color = _reduce_attribute(\n", | |
" on_states, 'rgb_color', reduce=_average_tuple)\n", | |
" if rgb_color is not None:\n", | |
" rgb_color = tuple(map(int, rgb_color))\n", | |
" white_value = _reduce_attribute(on_states, 'white_value')\n", | |
" color_temp = _reduce_attribute(on_states, 'color_temp')\n", | |
" min_mireds = _reduce_attribute(\n", | |
" states, 'min_mireds', default=154, reduce=min)\n", | |
" max_mireds = _reduce_attribute(\n", | |
" states, 'max_mireds', default=500, reduce=max)\n", | |
"\n", | |
" effect_list = None\n", | |
" all_effect_lists = list(_find_state_attributes(states, 'effect_list'))\n", | |
" if all_effect_lists:\n", | |
" # Merge all effects from all effect_lists with a union merge.\n", | |
" effect_list = list(set().union(*all_effect_lists))\n", | |
"\n", | |
" effect = None\n", | |
" all_effects = list(_find_state_attributes(states, 'effect'))\n", | |
" if all_effects:\n", | |
" # Report the most common effect.\n", | |
" effects_count = Counter(itertools.chain(all_effects))\n", | |
" effect = effects_count.most_common(1)[0][0]\n", | |
"\n", | |
" supported_features = 0\n", | |
" for support in _find_state_attributes(states, 'supported_features'):\n", | |
" # Merge supported features by emulating support for every feature\n", | |
" # we find.\n", | |
" supported_features |= support\n", | |
" \n", | |
" return (state, brightness, xy_color, rgb_color,\n", | |
" white_value, color_temp, min_mireds, max_mireds,\n", | |
" effect_list, effect, supported_features)\n", | |
" \n", | |
"def _find_state_attributes(states, key):\n", | |
" for state in states:\n", | |
" value = state.attributes.get(key)\n", | |
" if value is not None:\n", | |
" yield value\n", | |
"\n", | |
"\n", | |
"def _average(*args):\n", | |
" return sum(args) / len(args)\n", | |
"\n", | |
"\n", | |
"def _average_tuple(*args):\n", | |
" return tuple(sum(l) / len(l) for l in zip(*args))\n", | |
"\n", | |
"\n", | |
"def _reduce_attribute(states, key, default=None, reduce=_average):\n", | |
" attrs = list(_find_state_attributes(states, key))\n", | |
"\n", | |
" if not attrs:\n", | |
" return default\n", | |
"\n", | |
" return reduce(*attrs)\n", | |
"\n", | |
"\n", | |
"def _determine_on_off_state(states):\n", | |
" s_states = [state.state for state in states]\n", | |
"\n", | |
" if not s_states or all(state == 'UNAVAILABLE' for state in s_states):\n", | |
" return 'UNAVAILABLE'\n", | |
" elif any(state == 'ON' for state in s_states):\n", | |
" return 'ON'\n", | |
" elif all(state == 'UNKNOWN' for state in s_states):\n", | |
" return 'UNKNOWN'\n", | |
" return 'OFF'\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 79, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def g(states):\n", | |
" state_ = _determine_on_off_state(states)\n", | |
"\n", | |
" brightness = None\n", | |
" num_brightness = 0\n", | |
" xy_color = None\n", | |
" num_xy_color = 0\n", | |
" rgb_color = None\n", | |
" num_rgb_color = 0\n", | |
" white_value = None\n", | |
" num_white_value = 0\n", | |
" color_temp = None\n", | |
" num_color_temp = 0\n", | |
" min_mireds = 154\n", | |
" max_mireds = 500\n", | |
" effect_list = None\n", | |
" effect = None\n", | |
" supported_features = 0\n", | |
" \n", | |
" for state in states:\n", | |
" if state is None:\n", | |
" continue\n", | |
" \n", | |
" if state.attributes.get('min_mireds') is not None:\n", | |
" min_mireds = min(min_mireds, state.attributes['min_mireds'])\n", | |
" \n", | |
" if state.attributes.get('max_mireds') is not None:\n", | |
" max_mireds = min(max_mireds, state.attributes['max_mireds'])\n", | |
" \n", | |
" if state.attributes.get('effect') is not None:\n", | |
" effect = (effect or list()) + [state.attributes['effect']]\n", | |
" \n", | |
" if state.attributes.get('supported_features') is not None:\n", | |
" supported_features |= state.attributes['supported_features']\n", | |
" \n", | |
" if state.state != 'ON':\n", | |
" continue\n", | |
" \n", | |
" if state.attributes.get('brightness') is not None:\n", | |
" brightness = (brightness or 0) + state.attributes['brightness']\n", | |
" num_brightness += 1\n", | |
" \n", | |
" if state.attributes.get('xy_color') is not None:\n", | |
" remote = state.attributes['xy_color']\n", | |
" if xy_color is None:\n", | |
" xy_color = (remote[0], remote[1])\n", | |
" else:\n", | |
" xy_color = (xy_color[0] + remote[0], xy_color[1] + remote[1])\n", | |
" num_xy_color += 1\n", | |
" \n", | |
" if state.attributes.get('rgb_color') is not None:\n", | |
" remote = state.attributes['rgb_color']\n", | |
" if rgb_color is None:\n", | |
" rgb_color = (remote[0], remote[1], remote[2])\n", | |
" else:\n", | |
" rgb_color = (rgb_color[0] + remote[0], rgb_color[1] + remote[1], rgb_color[2] + remote[2])\n", | |
" num_rgb_color += 1\n", | |
" \n", | |
" if state.attributes.get('white_value') is not None:\n", | |
" white_value = (white_value or 0) + state.attributes['white_value']\n", | |
" num_white_value += 1\n", | |
" \n", | |
" if state.attributes.get('color_temp') is not None:\n", | |
" color_temp = (color_temp or 0) + state.attributes['color_temp']\n", | |
" num_color_temp += 1\n", | |
" \n", | |
" if state.attributes.get('effect_list') is not None:\n", | |
" effect_list = (effect_list or set()) | set(state.attributes['effect_list'])\n", | |
"\n", | |
" if brightness is not None:\n", | |
" brightness /= num_brightness\n", | |
" \n", | |
" if xy_color is not None:\n", | |
" xy_color = (xy_color[0] / num_xy_color, xy_color[1] / num_xy_color)\n", | |
" \n", | |
" if rgb_color is not None:\n", | |
" rgb_color = tuple(map(int, (rgb_color[0] / num_rgb_color, rgb_color[1] / num_rgb_color, rgb_color[2] / num_rgb_color)))\n", | |
" \n", | |
" if white_value is not None:\n", | |
" white_value /= num_white_value\n", | |
" \n", | |
" if color_temp is not None:\n", | |
" color_temp /= num_color_temp\n", | |
" \n", | |
" if effect_list is not None:\n", | |
" effect_list = list(effect_list)\n", | |
" \n", | |
" if effect is not None:\n", | |
" effects_count = Counter(itertools.chain(effect))\n", | |
" effect = effects_count.most_common(1)[0][0]\n", | |
" \n", | |
" return (state_, brightness, xy_color, rgb_color,\n", | |
" white_value, color_temp, min_mireds, max_mireds,\n", | |
" effect_list, effect, supported_features)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 80, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"('ON',\n", | |
" 255.0,\n", | |
" (1.0, 1.0),\n", | |
" (255, 255, 255),\n", | |
" 0.0,\n", | |
" 42.0,\n", | |
" 154,\n", | |
" 500,\n", | |
" ['None', 'Random'],\n", | |
" 'None',\n", | |
" 255)" | |
] | |
}, | |
"execution_count": 80, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"f(states)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 81, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"True" | |
] | |
}, | |
"execution_count": 81, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"f(states) == g(states)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 82, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"108 µs ± 2.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" | |
] | |
} | |
], | |
"source": [ | |
"%timeit f(states)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 83, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"121 µs ± 3.46 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" | |
] | |
} | |
], | |
"source": [ | |
"%timeit g(states)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.4" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Why not map all on_states into a 'combined' state first?
(just make each attribute into a list of values, order is not important right?)
So you would work with:
Then you only need to iterate the states once and can just pass each attributes list to your reduce function.
Oh, and that first (and faster) function a A LOT more readable. (that counts too imo)