【Gradio】Custom Components | Gradio组件关键概念 后端

Gradio组件关键概念

在本节中,我们将讨论Gradio中组件的一些重要概念。在开发自己的组件时,理解这些概念非常重要。否则,您的组件可能会与其他Gradio组件的行为大不相同!

✍️ 提示:如果你熟悉Gradio库的内部机制,例如每个组件的预处理(preprocess)和后处理(postprocess)方法,你可以跳过这一节。

交互式 vs 静态

Gradio中的每个组件都有一个静态变体,大多数也有一个交互式版本。当组件显示一个值,并且用户不能通过与之交互来更改该值时,使用静态版本。当用户能够通过与Gradio UI交互来更改值时,使用交互式版本。

让我们看一些例子:

# 导入gradio库
import gradio as gr


# 创建一个Blocks界面
with gr.Blocks() as demo:
   # 创建一个可交互的文本框
   gr.Textbox(value="Hello", interactive=True)
   # 创建一个静态文本框
   gr.Textbox(value="Hello", interactive=False)


# 启动demo界面
demo.launch()

这将显示两个文本框。唯一的区别:你将能够编辑顶部的Gradio组件的值,而底部的变体将无法编辑(即文本框将被禁用)。

也许更有趣的例子是Image组件:

# 导入gradio库
import gradio as gr


# 创建一个Blocks界面
with gr.Blocks() as demo:
   # 创建一个可交互的图片组件
   gr.Image(interactive=True)
   # 创建一个静态图片组件
   gr.Image(interactive=False)


# 启动demo界面
demo.launch()

组件的交互式版本要复杂得多 — 你可以上传图片或者从你的摄像头拍照 — 而静态版本只能用来显示图片。

并非每个组件都有一个明显的交互式版本。例如,gr.AnnotatedImage仅作为静态版本出现,因为没有方法可以交互式地更改注解或图片的值。

你需要记住的

如果组件被用作任何事件的输入,Gradio将使用该组件的交互式版本(如果可用);否则,将使用静态版本。

当你设计自定义组件时,你必须在Python类的构造函数中接受布尔值关键字interactive。在前端,你可能接受interactive属性,一个表示组件应该是静态还是交互式的布尔值。如果你在前端不使用这个属性,组件在交互式或静态模式下将看起来相同。

值以及它是如何被预处理/后处理的

组件最重要的属性是它的值。每个组件都有一个值。这个值通常由前端的用户设置(如果组件是交互式的)或显示给用户(如果它是静态的)。同时,当用户触发事件时,这个值也会发送到后端函数,或者在预测结束时由用户的函数返回。

所以这个值在很多地方被传递,但有时这个值的格式需要在前端和后端之间改变。看这个例子:

# 导入numpy和gradio库
import numpy as np
import gradio as gr


# 定义一个将图片转换为sepia色调的函数
def sepia(input_img):
    sepia_filter = np.array([
        [0.393, 0.769, 0.189], 
        [0.349, 0.686, 0.168], 
        [0.272, 0.534, 0.131]
    ])
    sepia_img = input_img.dot(sepia_filter.T)
    sepia_img /= sepia_img.max()
    return sepia_img


# 创建一个接受并返回图片的Gradio应用
demo = gr.Interface(sepia, gr.Image(shape=(200, 200)), "image")
# 启动demo应用
demo.launch()

这将创建一个Gradio应用,该应用有一个Image组件作为输入和输出。在前端,Image组件实际上会将文件上传到服务器并发送文件路径,但这在发送给用户的函数之前被转换为numpy数组。反过来,当用户从他们的函数返回一个numpy数组时,numpy数组被转换为文件,这样它就可以被发送到前端并由Image组件显示。

提示:默认情况下, Image 组件会将 numpy 数组发送到 python 函数,因为这是机器学习工程师的常用选择,尽管 Image 组件也支持使用 type 参数的其他格式。阅读 Image 文档了解更多信息。

每个组件都做两次转换:

  • preprocess:将值从前端发送的格式转换为Python函数期望的格式。这通常涉及从一个Web友好的JSON结构转换为一个Python原生数据结构,如numpy数组或PIL图像。Audio、Image组件是preprocess方法的好例子。

  • postprocess:将Python函数返回的值转换为前端期望的格式。这通常涉及从一个Python原生数据结构,如PIL图像转换为一个JSON结构。

你需要记住的

每个组件必须实现preprocesspostprocess方法。在罕见的事件中,如果不需要发生任何转换,简单地返回值即可。Textbox和Number是这种情况的例子。

作为组件作者,你控制前端显示的数据格式以及使用你的组件的人将接收的数据格式。想想一个Python开发者会觉得直观的人体工程学数据结构,并控制从一个Web友好的JSON数据结构(反之亦然)的转换,通过preprocesspostprocess

组件的“examples版本”

Gradio应用支持提供示例输入 — 这些在帮助用户开始使用你的Gradio应用时非常有用。在gr.Interface中,你可以使用examples关键字提供示例,在Blocks中,你可以使用特殊的gr.Examples组件提供示例。

在这个屏幕截图的底部,我们展示了一个小型的猎豹示例图片,当点击时,将在输入Image组件中填充相同的图片:

953d24c1b3797e190e52cb04b86215c6.png

要启用示例视图,您必须在 frontend 目录的顶部拥有以下两个文件:

  • Example.svelte:这对应于你的组件的“示例版本”

  • Index.svelte:这对应于“常规版本”

在后端,你通常不需要做任何事情。用户提供的示例值会使用前面描述的相同的.postprocess()方法进行处理。如果你想以不同的方式处理数据(例如,如果.postprocess()方法计算成本很高),那么你可以为你的自定义组件编写你自己的.process_example()方法,这将被使用。

Example.svelte文件和process_example()方法将在专门的前端https://www.gradio.app/guides/frontend和后端https://www.gradio.app/guides/backend指南中更深入地介绍。

你需要记住的

如果你期望你的组件被用作输入,定义一个“示例”视图很重要。

如果你不这样做,Gradio将使用一个默认的,但它不会像它可以的那样信息丰富!

结论

现在你知道了关于Gradio组件最重要的几点,你就可以开始设计和构建你自己的了!

配置您的自定义组件

自定义组件工作流程专注于约定优于配置,以减少您作为开发人员在开发自定义组件时需要做出的决策数量。话虽如此,您仍然可以配置自定义组件包和目录的某些方面。本指南将介绍如何操作。

 包名称 

默认情况下,所有自定义组件包都称为 gradio_<component-name> ,其中 component-name 是组件的 python 类的小写名称。

作为一个例子,让我们演示一下如何将一个组件的名称从 gradio_mytextbox 更改为 supertextbox 

  1. 修改 pyproject.toml 文件中的 name 。

[project]
name = "supertextbox"
  1. 将 pyproject.toml 中所有的 gradio_<component-name> 更改为 <component-name>

[tool.hatch.build]
artifacts = ["/backend/supertextbox/templates", "*.pyi"]


[tool.hatch.build.targets.wheel]
packages = ["/backend/supertextbox"]
  1. 将 backend/ 中的 gradio_<component-name> 目录重命名为 <component-name>

mv backend/gradio_mytextbox backend/supertextbox

✍️ 提示:记得更改`demo/app.py`中的导入语句!

顶级 Python 导出 

默认情况下,只有自定义组件的 python 类是顶级导出。这意味着当用户输入 from gradio_<component-name> import ... 时,唯一可用的类是自定义组件类。要添加更多类作为顶级导出,请修改 __all__ 属性在 __init__.py

from .mytextbox import MyTextbox
from .mytextbox import AdditionalClass, additional_function


__all__ = ['MyTextbox', 'AdditionalClass', 'additional_function']

Python 依赖项 

您可以通过修改 dependencies 键在 pyproject.toml 中来添加 python 依赖项

dependencies = ["gradio", "numpy", "PIL"]

✍️ 提示:添加依赖项时记得运行 `gradio cc install`!

JavaScript 依赖项 

你可以通过修改 frontend/package.json 中的 "dependencies" 键来添加 JavaScript 依赖项

"dependencies": {
    "@gradio/atoms": "0.2.0-beta.4",
    "@gradio/statustracker": "0.3.0-beta.6",
    "@gradio/utils": "0.2.0-beta.4",
    "your-npm-package": "<version>"
}

目录结构 

默认情况下,CLI 会将 Python 代码放在 backend 中,将 JavaScript 代码放在 frontend 中。不建议更改此结构,因为这使得潜在的贡献者能够轻松查看您的源代码并知道一切所在。然而,如果你确实想要更改,这就是你需要做的:

  1. 将 Python 代码放在你选择的子目录中。记得修改 pyproject.toml 中的 [tool.hatch.build] [tool.hatch.build.targets.wheel] 以匹配!

  2. 将 JavaScript 代码放置在您选择的子目录中。

  3. 在组件 python 类上添加 FRONTEND_DIR 属性。它必须是从定义类的文件到 JavaScript 目录位置的相对路径。

class SuperTextbox(Component):
    FRONTEND_DIR = "../../frontend/"

JavaScript 和 Python 目录必须位于同一个公共目录下!

 结论 

坚持默认设置将使其他人更容易理解和贡献您的自定义组件。毕竟,开源的美妙之处在于任何人都可以帮助改进您的代码!但如果您需要偏离默认设置,您知道该怎么做!

后端 🐍

本指南将涵盖实现自定义组件后端处理所需了解的所有内容。

应继承哪个类 

所有组件都继承自三个类别中的一个 Component 、 FormComponent 或 BlockContext 。你需要从其中一个继承,以便你的组件像所有其他 gradio 组件一样运作。当你从带有 gradio cc create --template 的模板开始时,你不需要担心选择哪一个,因为模板会使用正确的类别。为了完整性,以及在你需要从头开始制作自己的组件时,我们解释了每个类别的用途。

  • FormComponent :当你想要你的组件与其他 FormComponents 在相同的 Form 布局中被分组在一起时使用。 Slider 、 Textbox 和 Number 组件都是 FormComponents 。

  • BlockContext :当你想要在你的组件"内部"放置其他组件时使用。这启用了 with MyComponent() as component: 语法。

  • Component :在所有其他情况下使用。

    ✍️ 提示:如果你的组件支持流式输出,请继承 StreamingOutput 类。

    ✍️ 提示:如果你继承自 BlockContext ,你也需要将元类设置为 ComponentMeta 。请参见下面的示例。

from gradio.blocks import BlockContext  #从gradio库的blocks模块导入BlockContext类
from gradio.component_meta import ComponentMeta  # 从gradio库的component_meta模块导入ComponentMeta类


@document()  #一个装饰器,可能用于生成文档,但在这里没有给出实际实现
class Row(BlockContext, metaclass=ComponentMeta):  # 定义一个名为Row的类,继承自BlockContext,并使用ComponentMeta作为元类
    pass  # 类体部分为空,是一个空类定义

df388f6b7e496d8fe94880c129458534.png

你需要实现的方法 

当你继承这些类中的任何一个时,必须实现以下方法。否则当你实例化你的组件时,Python 解释器会抛出错误!

  preprocess 和 postprocess 

在关键概念指南中解释。它们处理从前端发送的数据到 Python 函数期望的格式的转换。

def preprocess(self, x: Any) -> Any:
        """
        Convert from the web-friendly (typically JSON) value in the frontend to the format expected by the python function.
        """
        return x


    def postprocess(self, y):
        """
        Convert from the data returned by the python function to the web-friendly (typically JSON) value expected by the frontend.
        """
        return y

process_example

接收原始的 Python 值,并返回应在应用程序中的示例预览中显示的修改后的值。如果没有提供,将使用 .postprocess() 方法代替。让我们看看来自 SimpleDropdown 组件的以下示例。

def process_example(self, input_data):  # 定义一个名为process_example的函数,接受两个参数:self和input_data
    # 列表解析:对于self.choices中的每一个元素c(c是一个由两个元素组成的列表或元组),如果c的第二个元素(c[1])与输入数据(input_data)相等,则取出c的第一个元素(c[0])并组成一个新的列表
    # next函数:从上述列表解析生成的列表中取出第一个元素;如果列表为空(即没有元素c满足c[1] == input_data),则返回None
    return next((c[0] for c in self.choices if c[1] == input_data), None)

8feeb5d1e7a5b13dd4d71f5a18f5d744.png

由于 self.choices 是对应于( display_name , value )的元组列表,这将把用户提供的值转换为显示值(或者如果值不在 self.choices 中,它将被转换为 None )。

api_info

preprocess 期望的值的 JSON-schema 表示。这通过 gradio 客户端支持 api 使用。如果你的组件指定了一个 data_model ,你不需要自己实现这个。以下部分的 data_model 。

def api_info(self) -> dict[str, list[str]]:
    """
    A JSON-schema representation of the value that the `preprocess` expects and the `postprocess` returns.
    """
    pass

example_payload

您组件的一个示例有效负载,例如,可以传递到组件的 .preprocess() 方法中的东西。示例输入显示在使用您的自定义组件的 Gradio 应用程序的 View API 页面上。必须是可 JSON 序列化的。如果您的组件期望一个文件,最好使用一个公开可访问的 URL。

def example_payload(self) -> Any:
    """
    The example inputs for this component for API usage. Must be JSON-serializable.
    """
    pass

example_value

您组件的一个示例值,例如,可以传递给组件的 .postprocess() 方法的东西。这在自定义组件开发中创建的默认应用程序中用作示例值。

def example_payload(self) -> Any:
    """
    The example inputs for this component for API usage. Must be JSON-serializable.
    """
    pass

flag

将组件的值写入可以存储在 csv 或 json 文件中用于标记的格式。如果您的组件指定了 data_model ,则无需自己实现此功能。以下部分中的 data_model 。

def flag(self, x: Any | GradioDataModel, flag_dir: str | Path = "") -> str:
    pass

read_from_flag

从存储在 csv 或 json 文件中的格式转换到组件的 python value 。如果你的组件指定了一个 data_model ,你不需要自己实现这个。以下部分的 data_model 。

def read_from_flag(
    self,
    x: Any,
) -> GradioDataModel | Any:
    """
    Convert the data from the csv or jsonl file into the component state.
    """
    return x

The data_model

data_model 是您定义组件值将如何存储在前端的预期数据格式。它指定了您的 preprocess 方法期望的数据格式以及 postprocess 方法返回的格式。为您的组件定义一个 data_model 不是必需的,但它极大地简化了创建自定义组件的过程。如果您定义了一个自定义组件,您只需要实现四个方法- preprocess 、 postprocess 、 example_payload 和 example_value !

您通过定义一个从 GradioModel 或 GradioRootModel 继承的 pydantic 模型来定义 data_model 。

这最好通过一个例子来解释。让我们看看核心的 Video 组件,它将视频数据存储为一个带有两个键 video 和 subtitles 的 JSON 对象,这两个键指向不同的文件。

from gradio.data_classes import FileData, GradioModel  # 从gradio.data_classes模块中导入FileData和GradioModel类


class VideoData(GradioModel):  # 定义一个名为VideoData的类,继承自GradioModel
    video: FileData  # video是一个FileData类型的属性,代表视频文件
    subtitles: Optional[FileData] = None  # subtitles是一个可选的FileData类型的属性,代表字幕文件,默认值为None


class Video(Component):  # 定义一个名为Video的类,继承自Component
    data_model = VideoData  # data_model是一个类属性(或者说是静态属性),其值为VideoData,表示该类处理的数据模型是VideoData

通过添加这四行代码,您的组件将自动实现 API 使用所需的方法、标记方法和示例缓存方法!它还具有自记录代码的附加优势。任何阅读您组件代码的人都会确切知道它期望的数据。

小贴士:如果您的组件期望从前端上传文件,您必须使用 FileData 模型!相关内容将在下一节中解释。

提示:在此处阅读 pydantic 文档。https://docs.pydantic.dev/latest/concepts/models/#basic-model-usage 

GradioModel 和 GradioRootModel 之间的区别在于 RootModel 不会将数据序列化为字典。例如, Names 模型会将数据序列化为 {'names': ['freddy', 'pete']} ,而 NamesRoot 模型则会将其序列化为 ['freddy', 'pete'] 。

from typing import List


class Names(GradioModel):
    names: List[str]


class NamesRoot(GradioRootModel):
    root: List[str]

即使你的组件不期望一个“复杂”的 JSON 数据结构,定义一个 GradioRootModel 也是有益的,这样你就不必担心实现 API 和标记方法了。

小贴士:使用 Python typing 库中的类来为你的模型定义类型。例如 ` List ` 而不是 ` list `。

 处理文件 

如果您的组件期望上传的文件作为输入,或者将保存的文件返回到前端,您必须使用 FileData 来在您的 data_model 中对文件进行类型定义。

当您使用 FileData 时:

  • Gradio 知道它应该允许将这个文件提供给前端。Gradio 自动阻止请求在运行服务器的计算机上提供任意文件。

  • Gradio 会自动将文件放入缓存中,以便不会保存文件的重复副本。

  • 客户端库将自动知道在发送请求之前应该上传输入文件。它们还会自动下载文件。

如果您不使用 FileData ,您的组件将无法按预期工作!

为您的组件添加事件触发器 

您组件的事件触发器在 EVENTS 类属性中定义。这是一个包含事件名称字符串的列表。在此列表中添加一个事件,将自动为您的组件添加一个具有相同名称的方法!

您可以从 gradio.events 导入 Events 枚举,以访问核心 gradio 组件中常用的事件。

例如,以下代码将在 MyComponent 类中定义 text_submit 、 file_upload 和 change 方法

from gradio.events import Events
from gradio.components import FormComponent


class MyComponent(FormComponent):


    EVENTS = [
        "text_submit",
        "file_upload",
        Events.change
    ]

小贴士:别忘了也要在 JavaScript 代码中处理这些事件!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/725849.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这三种电容在PCB设计中怎么摆放?

电容在高速 PCB 设计中起着重要的作用&#xff0c;通常也是 PCB 上用得多的器件。在 PCB 中&#xff0c;电容通 常分为滤波电容、去耦电容、储能电容等。 1 电源输出电容&#xff0c;滤波电容 我们通常把电源模块输入、输出回路的电容称为滤波电容。简单理解就是&#xff0c;…

Python基础-引用参数、斐波那契数列、无极分类

1.引用参数的问题 &#xff08;1&#xff09;列表&#xff08;list&#xff09; 引用参数&#xff0c;传地址的参数&#xff0c;即list1会因list2修改而改变。 list1 [1,2,3,4] list2 list1 print(list1) list2[2] 1 print(list2) print(list1)非引用参数&#xff0c;不传…

HTML静态网页成品作业(HTML+CSS+JS)——我的家乡福州介绍网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;使用Javacsript代码实现图片轮播&#xff0c;共有3个页面。 二、作品…

Acrel-2000ES储能能量管理系统方案

应用场合 电池厂商、储能集成商等厂家所生产的储能一体柜能集成箱

AI融资热潮:Genspark、Finaloop等初创吸金不断!中国气象局发布三大AI模型

文章推荐 AI日报&#xff5c;跃问App上架加入AI助理竞争&#xff01;GPTZero获千万美元A轮融资&#xff0c;创始人不到30岁&#xff01; AI日报&#xff5c;Luma推出AI视频模型&#xff0c;又一Sora级选手登场&#xff1f;SD3 Medium发布&#xff0c;图中文效果改善明显 ⭐️…

CentOS配置本地yum源

版本说明 操作系统版本&#xff1a;CentOS7.9 虚拟机版本 虚拟机打快照 首先给虚拟机打个快照&#xff0c;点击图下所示位置 命名快照之后&#xff0c;点击拍摄快照 可以参考图下所示进行管理和恢复快照 迁移原有yum源 先进入到/etc/yum.repos.d/ &#xff0c;可以看到有很多…

Linux 6.10也引进了蓝屏机制

众所周知&#xff0c;win死机后会有个蓝屏死机的故障提示页面&#xff0c;Linux 6.10 开始也将引入这个机制。 Linux 6.10 引入了一个新的 DRM Panic 处理程序基础设施&#xff0c;以便于在致命错误&#xff08;Panic&#xff09;发生时显示相关信息。 Linux 6.10 还在开发之…

SPI协议——读取外部SPI Flash ID

简介&#xff1a; 单片机型号&#xff1a;stm32l431rct6 SPI Flash型号&#xff1a;W25Q32JVSSIQ 使用软件&#xff1a;CubeIDE 1. W25Q32JVSSIQ简介 我们通过SPI协议来读取 SPI Flash的厂商ID和芯片独一无二的ID&#xff0c;查数据的芯片手册可以看到如下重要点&#xff1a; …

pytorch库 02 Anaconda、Jupyter常用命令及操作

文章目录 一、Anaconda Prompt1、conda常用命令2、pip常用命令 二、Jupyter1、Jupyter常用命令及基本操作2、Jupyter代码补全插件安装 一、Anaconda Prompt 1、conda常用命令 下列命令可以在Anaconda Prompt中输入。 清屏&#xff1a; cls 查看帮助&#xff1a; conda -h 查…

HTML静态网页成品作业(HTML+CSS)——手机电子商城网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

视频与音频的交响:探索达摩院VideoLLaMA 2的技术创新

一、简介 文章&#xff1a;https://arxiv.org/abs/2406.07476 代码&#xff1a;https://github.com/DAMO-NLP-SG/VideoLLaMA2 VideoLLaMA 2是由阿里巴巴集团的DAMO Academy团队开发的视频大型语言模型&#xff08;Video-LLM&#xff09;&#xff0c;旨在通过增强空间-时间建模…

分享:一个可爱的运行猫动画在你的窗口任务栏上

RunCat_for_windows&#xff1a;在 Windows 任务栏飞奔的“小猫”。这是一个用 C# 写的小工具&#xff0c;它会在 Windows 任务栏显示一只奔跑的小猫动画&#xff0c;CPU 使用率越高它跑得越快。 安装很简单 访问“发布”页面并下载 RunCat.exe。 或通过 Scoop&#xff08;x…

Android SurfaceFlinger——服务启动流程(二)

SurfaceFlinger 是 Android 系统中的一个核心服务&#xff0c;负责管理图形缓冲区的合成和屏幕显示&#xff0c;是 Android 图形系统的关键组件。 一、启动流程 SurfaceFlinger 作为一个系统服务&#xff0c;在 Android 启动早期由 init 进程通过 servicemanager 启动。它是作…

【漏洞复现】用友 NC NCFindWeb 任意文件读取漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

数学建模基础:统计模型

目录 前言 一、概率与统计基础 二、统计模型 三、Matlab统计工具箱 四、实例示范&#xff1a;市场调查分析 步骤 1&#xff1a;数据导入 步骤 2&#xff1a;数据可视化 步骤 3&#xff1a;建立多元线性回归模型 步骤 4&#xff1a;模型验证 步骤 5&#xff1a;模型应…

开发人员不要嗨,B端系统颜值不过关,可能进不去客户选择范围。

市面上形形色色的B端管理系统非常多&#xff0c;大家近身搏杀&#xff0c;这个市场已经成为了红海。也就是随便某一个功能领域&#xff0c;都有N的系统可以选择&#xff0c;这个时候你坚持B端系统功能使用就行&#xff0c;那就是自嗨了。现在是个过剩的时代&#xff0c;不是供不…

MaxWell实时监控Mysql并把数据写入到Kafka主题中

配置mysql 启用MySQL Binlog MySQL服务器的Binlog默认是未开启的&#xff0c;如需进行同步&#xff0c;需要先进行开启 修改MySQL配置文件/etc/my.cnf sudo vim/etc/my.cof 增加如下配置 注&#xff1a;MySQL Binlog模式 Statement-based&#xff1a;基于语句&#xff0c;…

数据分析第十二讲 数据可视化入门(一)

数据可视化入门&#xff08;一&#xff09; 在完成了对数据的透视之后&#xff0c;我们可以将数据透视的结果通过可视化的方式呈现出来&#xff0c;简单的说&#xff0c;就是将数据变成漂亮的统计图表&#xff0c;因为人类对颜色和形状会更加敏感&#xff0c;然后再进一步解读…

三步问题00

题目链接 三步问题 题目描述 注意点 n范围在[1, 1000000]之间结果可能很大&#xff0c;需要对结果模1000000007 解答思路 动态规划的思想根据dp[i - 1]、dp[i - 2]、dp[i - 3]推出dp[i]需要注意的是结果可能很大&#xff0c;在计算的过程中需要模1000000007防止越界 代码…

宋仕强论道之华强北假货之四

宋仕强论道之华强北假货之四&#xff0c;华强北虚标货。在华强北顾名思义就是“以少充多、以小充大&#xff0c;把低级当高级卖”。同样品牌和型号的产品&#xff0c;用民用级冒充工业级&#xff0c;工业级当军用级&#xff0c;军用级再当航天级&#xff0c;品牌型号都不变&…