Skip to content

Instantly share code, notes, and snippets.

@OttoWinter
Last active February 21, 2018 13:24
Show Gist options
  • Save OttoWinter/58ce0fd4c7743d233af8a10ed7ff940a to your computer and use it in GitHub Desktop.
Save OttoWinter/58ce0fd4c7743d233af8a10ed7ff940a to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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
}
@NovapaX
Copy link

NovapaX commented Feb 21, 2018

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:

on_states: {
   'brightness': [255, 232, 200]
    'xy_color': [[1.0, 1.0],[1.2, 1.2],[1.5, 1.1],
    'rgb_color': [[255, 255, 255],[255, 255, 255],[255, 255, 255]]
    'white_value': [0, 0, 0.1]
    'supported_features': [1, 2, 345]
})

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment