{"id":3660,"date":"2025-05-08T07:02:23","date_gmt":"2025-05-08T07:02:23","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/05\/08\/real-time-interactive-sentiment-analysis-in-python\/"},"modified":"2025-05-08T07:02:23","modified_gmt":"2025-05-08T07:02:23","slug":"real-time-interactive-sentiment-analysis-in-python","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/05\/08\/real-time-interactive-sentiment-analysis-in-python\/","title":{"rendered":"Real-Time Interactive Sentiment Analysis in Python"},"content":{"rendered":"<p>    Real-Time Interactive Sentiment Analysis in Python<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<figure class=\"wp-block-image size-full is-resized\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/04\/2025-04-23_23-11-15-ezgif.com-video-to-gif-converter.gif?ssl=1\" alt=\"\" class=\"wp-image-602629\" style=\"width:680px;height:auto\"><\/figure>\n<p class=\"wp-block-paragraph\"><mdspan datatext=\"el1746667834713\" class=\"mdspan-comment\">You know<\/mdspan> what the best part of being an engineer is? You can just build stuff. It\u2019s like a superpower. One rainy afternoon I had this random idea of creating a sentiment visualization of a text input with a smiley face that changes it\u2019s expression base on how positive the text is. The more positive the text, the happier the smiley looks. There are some interesting concepts to learn here, so let me guide you through how this project works!<\/p>\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n<p class=\"wp-block-paragraph\">To follow along, you need the following packages:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">customtkinter<\/li>\n<li class=\"wp-block-list-item\">\n<a href=\"https:\/\/towardsdatascience.com\/tag\/opencv\/\" title=\"Opencv\">Opencv<\/a>-python<\/li>\n<li class=\"wp-block-list-item\">torch<\/li>\n<li class=\"wp-block-list-item\">transformers<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Using <strong><a href=\"https:\/\/docs.astral.sh\/uv\/\">uv<\/a><\/strong>, you can add the dependencies with the following command:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\">uv add customtkinter opencv-<a href=\"https:\/\/towardsdatascience.com\/tag\/python\/\" title=\"Python\">Python<\/a> torch transformers<\/code><\/pre>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>NOTE:<\/strong> When using <em>uv<\/em> with torch you need to specify the index for the package. E.g if you want to use cuda, you need the following in your <code>pyproject.toml<\/code>:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-ini\">[[tool.uv.index]]\nname = \"pytorch-cu118\"\nurl = \"https:\/\/download.pytorch.org\/whl\/cu118\"\nexplicit = true\n\n[tool.uv.sources]\ntorch = [{ index = \"pytorch-cu118\" }]\ntorchvision = [{ index = \"pytorch-cu118\" }]<\/code><\/pre>\n<\/blockquote>\n<h2 class=\"wp-block-heading\">UI Layout Skeleton<\/h2>\n<p class=\"wp-block-paragraph\">For these types of projects I always like to start with a quick layout of the UI components. In this case the layout will be quite simple, there\u2019s a textbox with a single line at the top that fills the width and below it the canvas filling the rest of the available space. This will be where we draw the smiley face <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/s.w.org\/images\/core\/emoji\/15.0.3\/72x72\/1f642.png?ssl=1\" alt=\"\ud83d\ude42\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\"><\/p>\n<p class=\"wp-block-paragraph\">Using <code>customtkinter<\/code>, we can write the layout as follows:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">import customtkinter\n\nclass App(customtkinter.CTk):\n    def __init__(self) -&gt; None:\n        super().__init__()\n\n        self.title(\"<a href=\"https:\/\/towardsdatascience.com\/tag\/sentiment-analysis\/\" title=\"Sentiment Analysis\">Sentiment Analysis<\/a>\")\n        self.geometry(\"800x600\")\n\n        self.grid_columnconfigure(0, weight=1)\n        self.grid_rowconfigure(0, weight=0)\n        self.grid_rowconfigure(1, weight=1)\n\n        self.sentiment_text_var = customtkinter.StringVar(master=self, value=\"Love\")\n\n        self.textbox = customtkinter.CTkEntry(\n            master=self,\n            corner_radius=10,\n            font=(\"Consolas\", 50),\n            justify=\"center\",\n            placeholder_text=\"Enter text here...\",\n            placeholder_text_color=\"gray\",\n            textvariable=self.sentiment_text_var,\n        )\n        self.textbox.grid(row=0, column=0, padx=20, pady=20, sticky=\"nsew\")\n        self.textbox.focus()\n\n        self.image_display = CTkImageDisplay(self)\n        self.image_display.grid(row=1, column=0, padx=20, pady=20, sticky=\"nsew\")<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Unfortunately there\u2019s no good out of the box solution for drawing opencv frames on a UI element, so I built my own <code>CTkImageDisplay<\/code>. If you want to learn in detail how it works, check out <a href=\"https:\/\/towardsdatascience.com\/modern-gui-applications-for-computer-vision-in-python\/\">my previous post<\/a>. In short, I use a <code>CTKLabel<\/code> component and decouple the thread that updates the image from the GUI thread using a synchronization queue.<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image.png?ssl=1\" alt=\"\" class=\"wp-image-603263\"><\/figure>\n<h2 class=\"wp-block-heading\">Procedural Smiley<\/h2>\n<p class=\"wp-block-paragraph\">For our smiley face, we could use different discrete images for sentiment ranges, so for example having three images saved for <em>negative<\/em>, <em>neutral<\/em> and <em>positive<\/em>. However, to get a more fine-grained sentiment visualized, we would need more images and it quickly becomes infeasible and we will not be able to animate transitions between these images. <\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" height=\"315\" width=\"1024\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-1-1024x315.png?resize=1024%2C315&#038;ssl=1\" alt=\"discrete sentiment smiley face images\" class=\"wp-image-603264\"><\/figure>\n<p class=\"wp-block-paragraph\">A better approach is to generate the image of the smiley face procedurally at runtime. To keep it simple, we will only change the background color of the smiley, as well as the curve of its mouth.<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" height=\"369\" width=\"1024\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-2-1024x369.png?resize=1024%2C369&#038;ssl=1\" alt=\"continuous sentiment score smiley face images\" class=\"wp-image-603265\"><\/figure>\n<p class=\"wp-block-paragraph\">First we need to generate a canvas image, on which we can draw the smiley.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">def create_sentiment_image(positivity: float, image_size: tuple[int, int]) -&gt; np.ndarray:\n    \"\"\"\n    Generates a sentiment image based on the positivity score.\n    This draws a smiley with its expression based on the positivity score.\n\n    Args:\n        positivity: A float representing the positivity score in the range [-1, 1].\n        image_size: A tuple representing the size of the image (width, height).\n\n    Returns:\n        A string representing the path to the generated sentiment image.\n    \"\"\"\n    width, height = image_size\n    frame = np.zeros((height, width, 4), dtype=np.uint8)\n\n    # TODO: draw smiley\n\n    return frame<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Our image should be transparent outside of the smiley face, so we need 4 color channels, the last one will be the alpha channel. Since OpenCV images are represented as <code>numpy<\/code> arrays with <em>unsigned 8-bit integers<\/em>, we create the image using the <code>np.uint8<\/code> data type. Remember that the arrays are stored <em>y-first<\/em>, so the <code>height<\/code> of the <code>image_size<\/code> is passed first to the array creation<\/p>\n<p class=\"wp-block-paragraph\">We can define some variables for the dimensions and colors of our smiley that will be helpful while drawing.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">    color_outline = (80,) * 3 + (255,)  # gray\n    thickness_outline = min(image_size) \/\/ 30\n    center = (width \/\/ 2, height \/\/ 2)\n    radius = min(image_size) \/\/ 2 - thickness_outline<\/code><\/pre>\n<p class=\"wp-block-paragraph\">The background color of the smiley face should be red for negative sentiments and green for positive sentiments. To achieve this with a uniform brightness across the transition, we can use the HSV color space and simply interpolate the hue between 0% and 30%.<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-16.png?ssl=1\" alt=\"\" class=\"wp-image-603286\"><\/figure>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">color_bgr = color_hsv_to_bgr(\n    hue=(positivity + 1) \/ 6, # positivity [-1,1] -&gt; hue [0,1\/3]\n    saturation=0.5,\n    value=1,\n)\ncolor_bgra = color_bgr + (255,)<\/code><\/pre>\n<p class=\"wp-block-paragraph\">We need to make sure to make the color fully opaque by adding a 100% alpha value in fourth channel. Now we can draw our smiley face circle with a border.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">cv2.circle(frame, center, radius, color_bgra, -1) # Fill\ncv2.circle(frame, center, radius, color_outline, thickness_outline) # Border<\/code><\/pre>\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-20.png?ssl=1\" alt=\"\" class=\"wp-image-603291\" style=\"width:248px;height:auto\"><\/figure>\n<p class=\"wp-block-paragraph\">So far so good, now we can add the eyes. We calculate an offset from the center to the left and right to place the two eyes symmetrically.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\"># calculate the position of the eyes\neye_radius = radius \/\/ 5\neye_offset_x = radius \/\/ 3\neye_offset_y = radius \/\/ 4\neye_left = (center[0] - eye_offset_x, center[1] - eye_offset_y)\neye_right = (center[0] + eye_offset_x, center[1] - eye_offset_y)\n\ncv2.circle(frame, eye_left, eye_radius, color_outline, -1)\ncv2.circle(frame, eye_right, eye_radius, color_outline, -1)<\/code><\/pre>\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-22.png?ssl=1\" alt=\"\" class=\"wp-image-603293\" style=\"width:250px;height:auto\"><\/figure>\n<p class=\"wp-block-paragraph\">Now on to the challenging part, the mouth. The shape of the mouth will be a parabola scaled appropriately. We can simply multiply the standard parabola <code>y=x\u00b2<\/code> with the positivity score.<\/p>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" height=\"659\" width=\"1024\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-24-1024x659.png?resize=1024%2C659&#038;ssl=1\" alt=\"\" class=\"wp-image-603295\"><\/figure>\n<p class=\"wp-block-paragraph\">In the end the line will be drawn using <code>cv2.polylines<\/code>, which needs xy coordinate pairs. Using <code>np.linspace<\/code> we generate 100 points on the x-axis and the <code>polyval<\/code> function to calculate the according y values of the polygon.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\"># mouth parameters\nmouth_wdith = radius \/\/ 2\nmouth_height = radius \/\/ 3\nmouth_offset_y = radius \/\/ 3\nmouth_center_y = center[1] + mouth_offset_y + positivity * mouth_height \/\/ 2\nmouth_left = (center[0] - mouth_wdith, center[1] + mouth_offset_y)\nmouth_right = (center[0] + mouth_wdith, center[1] + mouth_offset_y)\n\n# calculate points of polynomial for the mouth\nply_points_t = np.linspace(-1, 1, 100)\nply_points_y = np.polyval([positivity, 0, 0], ply_points_t) # y=positivity*x\u00b2\n\nply_points = np.array(\n    [\n        (\n            mouth_left[0] + i * (mouth_right[0] - mouth_left[0]) \/ 100,\n            mouth_center_y - ply_points_y[i] * mouth_height,\n        )\n        for i in range(len(ply_points_y))\n    ],\n    dtype=np.int32,\n)\n\n# draw the mouth\ncv2.polylines(\n    frame,\n    [ply_points],\n    isClosed=False,\n    color=color_outline,\n    thickness=int(thickness_outline * 1.5),\n)<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Et voil\u00e0, we have a procedural smiley face!<\/p>\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-25.png?ssl=1\" alt=\"\" class=\"wp-image-603296\" style=\"width:250px;height:auto\"><\/figure>\n<p class=\"wp-block-paragraph\">To test the function, I wrote a quick test case using <code>pytest<\/code> that saves the smiley faces with different sentiment scores:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">from pathlib import Path\n\nimport cv2\nimport numpy as np\nimport pytest\n\nfrom sentiment_analysis.utils import create_sentiment_image\n\nIMAGE_SIZE = (512, 512)\n\n\n@pytest.mark.parametrize(\n    \"positivity\",\n    np.linspace(-1, 1, 5),\n)\ndef test_sentiments(visual_output_path: Path, positivity: float) -&gt; None:\n    \"\"\"\n    Test the smiley face generation.\n    \"\"\"\n    image = create_sentiment_image(positivity, IMAGE_SIZE)\n\n    assert image.shape == (IMAGE_SIZE[1], IMAGE_SIZE[0], 4)\n\n    # assert center pixel is opaque\n    assert image[IMAGE_SIZE[1] \/\/ 2, IMAGE_SIZE[0] \/\/ 2, 3] == 255\n\n    # save the image for visual inspection\n    positivity_num_0_100 = int((positivity + 1) * 50)\n    image_fn = f\"smiley_{positivity_num_0_100}.png\"\n    cv2.imwrite(str(visual_output_path \/ image_fn), image)\n<\/code><\/pre>\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" height=\"252\" width=\"1024\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/image-26-1024x252.png?resize=1024%2C252&#038;ssl=1\" alt=\"\" class=\"wp-image-603297\"><\/figure>\n<h2 class=\"wp-block-heading\">Sentiment Analysis<\/h2>\n<p class=\"wp-block-paragraph\">To determine how happy or sad our smiley should look like, we first need to analyze the text input and calculate a <em>sentiment<\/em>. This task is called <strong>sentiment analysis<\/strong>. We will use a pre-trained transformer model to predict a classification score for the classes <em>NEGATIVE<\/em>, <em>NEUTRAL<\/em> and <em>POSITIVE<\/em>. We can then fuse the confidence scores of these classes to calculate a final sentiment score between -1 and +1.<\/p>\n<p class=\"wp-block-paragraph\">Using the pipeline from the transformers library, we can define processing pipeline based on a <a href=\"https:\/\/huggingface.co\/cardiffnlp\/twitter-roberta-base-sentiment\">pre-trained model from huggingface.<\/a> Using the <code>top_k<\/code> parameter, we can specify how many classification results should be returned. Since we want all three classes, we set it to 3.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">from transformers import pipeline\n\nmodel_name = \"cardiffnlp\/twitter-roberta-base-sentiment\"\n\nsentiment_pipeline = pipeline(\n    task=\"sentiment-analysis\",\n    model=model_name,\n    top_k=3,\n)<\/code><\/pre>\n<p class=\"wp-block-paragraph\">To run the sentiment analysis, we can call the pipeline with a string argument. This will return a list of results with a single element, so we need to unpack the first element.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">results = self.sentiment_pipeline(text)\n\n# [\n#     [\n#         {\"label\": \"LABEL_2\", \"score\": 0.5925878286361694},\n#         {\"label\": \"LABEL_1\", \"score\": 0.3553399443626404},\n#         {\"label\": \"LABEL_0\", \"score\": 0.05207228660583496},\n#     ]\n# ]\n\nfor label_score_dict in results[0]:\n    label: str = label_score_dict[\"label\"]\n    score: float = label_score_dict[\"score\"]<\/code><\/pre>\n<p class=\"wp-block-paragraph\">We can define a label mapping, that tells us how each confidence score affects the final sentiment. Then we can aggregate the positivity over all confidence scores.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">label_mapping = {\"LABEL_0\": -1, \"LABEL_1\": 0, \"LABEL_2\": 1}\n\npositivity = 0.0\nfor label_score_dict in results[0]:\n    label: str = label_score_dict[\"label\"]\n    score: float = label_score_dict[\"score\"]\n\n    if label in label_mapping:\n        positivity += label_mapping[label] * score<\/code><\/pre>\n<p class=\"wp-block-paragraph\">To test our pipeline, we can wrap it in a class and run some tests using <code>pytest<\/code>. We verify that sentences with a positive sentiment have a score greater than zero and vice versa sentences with a negative sentiment should have a score below zero.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">import pytest\n\nfrom sentiment_analysis.sentiment_pipeline import SentimentAnalysisPipeline\n\n\n@pytest.fixture\ndef sentiment_pipeline() -&gt; SentimentAnalysisPipeline:\n    \"\"\"\n    Fixture to create a SentimentAnalysisPipeline instance.\n    \"\"\"\n    return SentimentAnalysisPipeline(\n        model_name=\"cardiffnlp\/twitter-roberta-base-sentiment\",\n        label_mapping={\"LABEL_0\": -1.0, \"LABEL_1\": 0.0, \"LABEL_2\": 1.0},\n    )\n\n\n@pytest.mark.parametrize(\n    \"text_input\",\n    [\n        \"I love this!\",\n        \"This is awesome!\",\n        \"I am so happy!\",\n        \"This is the best day ever!\",\n        \"I am thrilled with the results!\",\n    ],\n)\ndef test_sentiment_analysis_pipeline_positive(\n    sentiment_pipeline: SentimentAnalysisPipeline, text_input: str\n) -&gt; None:\n    \"\"\"\n    Test the sentiment analysis pipeline with a positive input.\n    \"\"\"\n    assert (\n        sentiment_pipeline.run(text_input) &gt; 0.0\n    ), \"Expected positive sentiment score.\"\n\n\n@pytest.mark.parametrize(\n    \"text_input\",\n    [\n        \"I hate this!\",\n        \"This is terrible!\",\n        \"I am so sad!\",\n        \"This is the worst day ever!\",\n        \"I am disappointed with the results!\",\n    ],\n)\ndef test_sentiment_analysis_pipeline_negative(\n    sentiment_pipeline: SentimentAnalysisPipeline, text_input: str\n) -&gt; None:\n    \"\"\"\n    Test the sentiment analysis pipeline with a negative input.\n    \"\"\"\n    assert (\n        sentiment_pipeline.run(text_input) &lt; 0.0\n    ), \"Expected negative sentiment score.\"\n<\/code><\/pre>\n<h2 class=\"wp-block-heading\">Integration<\/h2>\n<p class=\"wp-block-paragraph\">Now the last part that is missing, is simply hooking up the text box to our sentiment pipeline and updating the displayed image with the corresponding smiley face. We can add a <code>trace<\/code> to the text variable, which will run the sentiment pipeline in a new thread managed by a thread pool, to prevent the UI from freezing while the pipeline is running.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\">class App(customtkinter.CTk):\n    def __init__(self, sentiment_analysis_pipeline: SentimentAnalysisPipeline) -&gt; None:\n        super().__init__()\n        self.sentiment_analysis_pipeline = sentiment_analysis_pipeline\n\n        ...\n\n        self.sentiment_image = None\n\n        self.sentiment_text_var = customtkinter.StringVar(master=self, value=\"Love\")\n        self.sentiment_text_var.trace_add(\"write\", lambda *_: self.on_sentiment_text_changed())\n\n        ...\n\n        self.update_sentiment_pool = ThreadPool(processes=1)\n\n        self.on_sentiment_text_changed()\n\n    def on_sentiment_text_changed(self) -&gt; None:\n        \"\"\"\n        Callback function to handle text changes in the textbox.\n        \"\"\"\n        new_text = self.sentiment_text_var.get()\n\n        self.update_sentiment_pool.apply_async(\n            self._update_sentiment,\n            (new_text,),\n        )\n\n    def _update_sentiment(self, new_text: str) -&gt; None:\n        \"\"\"\n        Update the sentiment image based on the new text input.\n        This function is run in a separate process to avoid blocking the main thread.\n\n        Args:\n            new_text: The new text input from the user.\n        \"\"\"\n        positivity = self.sentiment_analysis_pipeline.run(new_text)\n\n        self.sentiment_image = create_sentiment_image(\n            positivity,\n            self.image_display.display_size,\n        )\n\n        self.image_display.update_frame(self.sentiment_image)\n\n\ndef main() -&gt; None:\n    # Initialize the sentiment analysis pipeline\n    sentiment_analysis = SentimentAnalysisPipeline(\n        model_name=\"cardiffnlp\/twitter-roberta-base-sentiment\",\n        label_mapping={\"LABEL_0\": -1, \"LABEL_1\": 0, \"LABEL_2\": 1},\n    )\n\n    app = App(sentiment_analysis)\n    app.mainloop()\n<\/code><\/pre>\n<p class=\"wp-block-paragraph\">And finally the smiley is visualized in the application and changes dynamically with the sentiment of the text input!<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/05\/2025-05-04_17-39-55-ezgif.com-video-to-gif-converter.gif?ssl=1\" alt=\"\" class=\"wp-image-603285\"><\/figure>\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-dotted\">\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-ornamental\">\n<p class=\"has-text-align-center wp-block-paragraph\">For the full implementation and more details, checkout the project repository on GitHub:<\/p>\n<p class=\"has-text-align-center wp-block-paragraph\"><a href=\"https:\/\/github.com\/trflorian\/sentiment-analysis-viz\">https:\/\/github.com\/trflorian\/sentiment-analysis-viz<\/a><\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-ornamental\">\n<p class=\"has-text-align-center wp-block-paragraph\"><em>All visualizations in this post were created by the author.<\/em><\/p>\n<p>The post <a href=\"https:\/\/towardsdatascience.com\/real-time-interactive-sentiment-analysis-in-python\/\">Real-Time Interactive Sentiment Analysis in Python<\/a> appeared first on <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a>.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Florian Trautweiler<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/towardsdatascience.com\/real-time-interactive-sentiment-analysis-in-python\/\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Real-Time Interactive Sentiment Analysis in Python You know what the best part of being an engineer is? You can just build stuff. It\u2019s like a superpower. One rainy afternoon I had this random idea of creating a sentiment visualization of a text input with a smiley face that changes it\u2019s expression base on how positive [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,83,82,87,2601,157,1408],"tags":[2602,508,834],"class_list":["post-3660","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-data-science","category-data-visualization","category-llm","category-opencv","category-python","category-sentiment-analysis","tag-customtkinter","tag-self","tag-text"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/3660"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=3660"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/3660\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=3660"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=3660"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=3660"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}