如何在熊猫数据框输出中定义索引或点击行数?

Posted

技术标签:

【中文标题】如何在熊猫数据框输出中定义索引或点击行数?【英文标题】:How could define index or number of clicked row in pandas dataframe output? 【发布时间】:2019-09-11 04:59:25 【问题描述】:

我想在 ipywidgets 上制作一些表格,在其输入中显示特定行的 pandas 数据框的值。

为了使行选择和以更具交互性的形式显示,我需要以某种方式单击显示的数据框。实际上,我希望能够识别最后点击的数据框行的索引或数量。

附言 我见过像 ipysheet 和其他工具这样的工具,但我认为它们仍然太新,尽管 ipysheet 将来可能会非常强大。 所以这些工具不适用于这项任务。

【问题讨论】:

对社区来说似乎是个棘手的问题) 你如何使用这个ipywidgets?您使用 Excel 之类的表单吗?你在 jupyter 中使用它吗? 【参考方案1】:

我遇到了类似的问题,qgrid 对我来说太过分了。

在 jupyter 中的工作(灵感来自 add click feature on each row in html table):

    注入的脚本将处理程序添加到数据帧表的每个单元格(由id='T_table'Styler.set_uuid 函数标记)。

    当一个单元格被点击时典型的处理程序:

      查找指定的输入元素(隐藏并由占位符“未定义”标记),

      为单击的单元格的类名设置找到的输入的 value 属性,

      触发“更改”事件。

    之后,点击单元格的类名(pandas 使它们包含必要的行/列信息)可用于 ipywidgets 框架,并可在 python 中处理。

备注:

    在调用脚本之前需要一定的超时时间。对于更大的表,所需的超时时间可能会更大。

    逻辑有点复杂,因为我使用的将js-values引入python世界的方法是通过ipywidgets.Text

    添加第 2.1 项以防 DOM 在启动后重新呈现。

import pandas as pd
import ipywidgets as wgt
from IPython.display import display, HTML
import re

# javascript-part
script = """
<script>
var input
var xpath = "//input[contains(@placeholder,'undefined')]";

function addHandlers() 
    input = document.evaluate(xpath, document, null, 
        XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    input.setAttribute("hidden","");

    var table = document.querySelector("#T_table");
    var headcells = [].slice.call(table.getElementsByTagName("th"));
    var datacells = [].slice.call(table.getElementsByTagName("td"));
    var cells = headcells.concat(datacells);
    for (var i=0; i < cells.length; i++) 
       var createClickHandler = function(cell) 
         return function()  
            input = document.evaluate(xpath, document, null,
                XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            input.value = cell.className; 
            var event = new Event('change',  bubbles: true );
            input.dispatchEvent(event);
      
      cells[i].onclick = createClickHandler(cells[i]);
    ;


window.onload = setTimeout(addHandlers, 500);
</script>
"""
display(HTML(script))

# ipywidgets-part
newdf = pd.DataFrame(data=
    '1': [11,21,31,41], '2': [12,22,32,42], '3': [13,23,33,43],
    '4': [14,24,34,44], '5': [15,25,35,45], '6': [16,26,36,46],
    '7': [17,27,37,47], '8': [18,28,38,48], '9': [19,29,39,49],
    '10': [110,210,310,410],'11': [111,211,311,411],
)

html = newdf.style.\
    set_uuid('table') 

def on_change(change):
    cls = change['new'].split(' ')
    if len(cls) == 2: 
        place.value, row.value = cls
        col.value = '0'
    elif len(cls) == 3: 
        place.value, txtrow, txtcol = cls
        res = re.search(r'\d+',txtrow).group(0)
        row.value = str(int(res)+1)
        res = re.search(r'\d+',txtcol).group(0)
        col.value = str(int(res)+1)
    else:
        place.value, row.value, col.value = ['unknown']*3

status = wgt.Text(placeholder='undefined',layout='font-size':'6px') 
status.observe(on_change,names=['value'])

table = wgt.Output()
with table: display(html)

layout = 'width':'192px'
row = wgt.Text(layout=layout,description='row')
col = wgt.Text(layout=layout,description='col')
place = wgt.Text(layout=layout,description='place')
body = wgt.HBox([table,wgt.VBox([place,row,col])])

wgt.VBox([body,status])

【讨论】:

这是一个非常好的实现,我只是想知道被点击的行是否可以在被点击后保持突出显示【参考方案2】:

你试过qgrid吗?

它在 github 上的定义是:一个交互式网格,用于在 Jupyter 笔记本中排序、过滤和编辑 DataFrames

【讨论】:

【参考方案3】:

带有 on_click 事件处理程序的 Jupyter 中的 Pandas 输出

点击行时:

行高亮 提供给 on_click 的行号 以打印行号和内容为例

基于@dzenny 的回答

import pandas as pd
import ipywidgets as wgt
from IPython.display import display, HTML
import re

# javascript-part
script = """
<style>tr.selected background-color:#00FFFF!important</style>
<script>
(function () 
    var input = document.evaluate("//input[contains(@placeholder,'undefined')]", document, null, 
        XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    input.setAttribute("hidden","");

    var table = document.querySelector("#T_table");
    var rows = Array.from(table.querySelectorAll('tbody > tr'));    
    var cells = Array.from(table.querySelectorAll("td"));
    for (var i=0; i < cells.length; i++) 
       var createClickHandler = function(cell) 
         return function()  
            input.value = cell.className; 
            var event = new Event('change',  bubbles: true );
            input.dispatchEvent(event);
            rows.forEach(el => el.classList.remove('selected')) 
            cell.parentElement.classList.add('selected')
      
      cells[i].onclick = createClickHandler(cells[i]);
    ;
)();
</script>
"""

# ipywidgets-part
df = pd.DataFrame(data=
    '1': [11,21,31,41], '2': [12,22,32,42], '3': [13,23,33,43],
    '4': [14,24,34,44], '5': [15,25,35,45], '6': [16,26,36,46],
    '7': [17,27,37,47], '8': [18,28,38,48], '9': [19,29,39,49],
    '10': [110,210,310,410],'11': [111,211,311,411])

html = df.style.set_uuid('table')

def on_click(change):
    cls = change['new'].split(' ')
    row = int(re.search(r'\d+',cls[1]).group(0))
    print('row=', row)
    print(df.iloc[[row]])

click_handler = wgt.Text(placeholder='undefined')
click_handler.observe(on_click,names=['value'])

out = wgt.Output()
with out: display(html)

display(out,click_handler,HTML(script))

【讨论】:

真的很有帮助。我在 JupyterLab 3.2.5 中没有看到打印,但是在阅读了 GitHub 上的第 2148 期后,我发现我需要重新排序代码,因此在 out 之前创建 on_click 并在定义回调之前添加 @out.capture()所以显示回调中的打印。

以上是关于如何在熊猫数据框输出中定义索引或点击行数?的主要内容,如果未能解决你的问题,请参考以下文章

熊猫(python):如何将列添加到数据框以进行索引?

如何按字符串索引上的自定义顺序对熊猫数据框进行排序

熊猫如何通过数据框列值获取行索引

如何计算熊猫数据框中每组的行数并将其添加到原始数据中

如何在熊猫数据框中插入列名? [复制]

如何在函数中重新索引熊猫数据框?