code: def svg_force_plot(
      values, base_values, fx, tokens, uuid, xmin, xmax, output_name
  ):
      def xpos(xval):
          return 100 * (xval - xmin) / (xmax - xmin + 1e-8)
  
      s = ""
      s += '<svg width="100%" height="80px">'
  
      ### x-axis marks ###
  
      # draw x axis line
      s += '<line x1="0" y1="33" x2="100%" y2="33" style="stroke:rgb(150,150,150);stroke-width:1" />'
  
      # draw base value
      def draw_tick_mark(xval, label=None, bold=False, backing=False):
          s = ""
          s += f'<line x1="{xpos(xval)}%" y1="33" x2="{xpos(xval)}%" y2="37" style="stroke:rgb(150,150,150);stroke-width:1" />'
          if not bold:
              if backing:
                  s += f'<text x="{xpos(xval)}%" y="27" font-size="13px" style="stroke:#ffffff;stroke-width:8px;" fill="rgb(255,255,255)" dominant-baseline="bottom" text-anchor="middle">{xval:g}</text>'
              s += f'<text x="{xpos(xval)}%" y="27" font-size="12px" fill="rgb(120,120,120)" dominant-baseline="bottom" text-anchor="middle">{xval:g}</text>'
          else:
              if backing:
                  s += f'<text x="{xpos(xval)}%" y="27" font-size="13px" style="stroke:#ffffff;stroke-width:8px;" font-weight="bold" fill="rgb(255,255,255)" dominant-baseline="bottom" text-anchor="middle">{xval:g}</text>'
              s += f'<text x="{xpos(xval)}%" y="27" font-size="13px" font-weight="bold" fill="rgb(0,0,0)" dominant-baseline="bottom" text-anchor="middle">{xval:g}</text>'
          if label is not None:
              s += f'<text x="{xpos(xval)}%" y="10" font-size="12px" fill="rgb(120,120,120)" dominant-baseline="bottom" text-anchor="middle">{label}</text>'
          return s
  
      xcenter = round(
          (xmax + xmin) / 2,
          int(round(1 - np.log10(xmax - xmin + 1e-8))),
      )
      s += draw_tick_mark(xcenter)
      #    np.log10(xmax - xmin)
  
      tick_interval = round(
          (xmax - xmin) / 7,
          int(round(1 - np.log10(xmax - xmin + 1e-8))),
      )
  
      # tick_interval = (xmax - xmin) / 7
      side_buffer = (xmax - xmin) / 14
      for i in range(1, 10):
          pos = xcenter - i * tick_interval
          if pos < xmin + side_buffer:
              break
          s += draw_tick_mark(pos)
      for i in range(1, 10):
          pos = xcenter + i * tick_interval
          if pos > xmax - side_buffer:
              break
          s += draw_tick_mark(pos)
      s += draw_tick_mark(base_values, label="base value", backing=True)
      s += draw_tick_mark(
          fx,
          bold=True,
          label=f'f<tspan baseline-shift="sub" font-size="8px">{output_name}</tspan>(inputs)',
          backing=True,
      )
  
      ### Positive value marks ###
  
      red = tuple(colors.red_rgb * 255)
      light_red = (255, 195, 213)
  
      # draw base red bar
      x = fx - values[values > 0].sum()
      w = 100 * values[values > 0].sum() / (xmax - xmin + 1e-8)
      s += f'<rect x="{xpos(x)}%" width="{w}%" y="40" height="18" style="fill:rgb{red}; stroke-width:0; stroke:rgb(0,0,0)" />'
  
      # draw underline marks and the text labels
      pos = fx
      last_pos = pos
      inds = [i for i in np.argsort(-np.abs(values)) if values[i] > 0]
      for i, ind in enumerate(inds):
          v = values[ind]
          pos -= v
  
          # a line under the bar to animate
          s += f'<line x1="{xpos(pos)}%" x2="{xpos(last_pos)}%" y1="60" y2="60" id="_fb_{uuid}_ind_{ind}" style="stroke:rgb{red};stroke-width:2; opacity: 0"/>'
  
          # the text label cropped and centered
          s += f'<text x="{(xpos(last_pos) + xpos(pos))/2}%" y="71" font-size="12px" id="_fs_{uuid}_ind_{ind}" fill="rgb{red}" style="opacity: 0" dominant-baseline="middle" text-anchor="middle">{values[ind].round(3)}</text>'
  
          # the text label cropped and centered
          s += f'<svg x="{xpos(pos)}%" y="40" height="20" width="{xpos(last_pos) - xpos(pos)}%">'
          s += '  <svg x="0" y="0" width="100%" height="100%">'
          s += f'    <text x="50%" y="9" font-size="12px" fill="rgb(255,255,255)" dominant-baseline="middle" text-anchor="middle">{tokens[ind].strip()}</text>'
          s += "  </svg>"
          s += "</svg>"
  
          last_pos = pos
  
      # draw the divider padding (which covers the text near the dividers)
      pos = fx
      for i, ind in enumerate(inds):
          v = values[ind]
          pos -= v
  
          if i != 0:
              for j in range(4):
                  s += f'<g transform="translate({2*j-8},0)">'
                  s += f'  <svg x="{xpos(last_pos)}%" y="40" height="18" overflow="visible" width="30">'
                  s += f'    <path d="M 0 -9 l 6 18 L 0 25" fill="none" style="stroke:rgb{red};stroke-width:2" />'
                  s += "  </svg>"
                  s += "</g>"
  
          if i + 1 != len(inds):
              for j in range(4):
                  s += f'<g transform="translate({2*j-0},0)">'
                  s += f'  <svg x="{xpos(pos)}%" y="40" height="18" overflow="visible" width="30">'
                  s += f'    <path d="M 0 -9 l 6 18 L 0 25" fill="none" style="stroke:rgb{red};stroke-width:2" />'
                  s += "  </svg>"
                  s += "</g>"
  
          last_pos = pos
  
      # center padding
      s += f'<rect transform="translate(-8,0)" x="{xpos(fx)}%" y="40" width="8" height="18" style="fill:rgb{red}"/>'
  
      # cover up a notch at the end of the red bar
      pos = fx - values[values > 0].sum()
      s += '<g transform="translate(-11.5,0)">'
      s += f'  <svg x="{xpos(pos)}%" y="40" height="18" overflow="visible" width="30">'
      s += '    <path d="M 10 -9 l 6 18 L 10 25 L 0 25 L 0 -9" fill="#ffffff" style="stroke:rgb(255,255,255);stroke-width:2" />'
      s += "  </svg>"
      s += "</g>"
  
      # draw the light red divider lines and a rect to handle mouseover events
      pos = fx
      last_pos = pos
      for i, ind in enumerate(inds):
          v = values[ind]
          pos -= v
  
          # divider line
          if i + 1 != len(inds):
              s += '<g transform="translate(-1.5,0)">'
              s += f'  <svg x="{xpos(last_pos)}%" y="40" height="18" overflow="visible" width="30">'
              s += f'    <path d="M 0 -9 l 6 18 L 0 25" fill="none" style="stroke:rgb{light_red};stroke-width:2" />'
              s += "  </svg>"
              s += "</g>"
  
          # mouse over rectangle
          s += f'<rect x="{xpos(pos)}%" y="40" height="20" width="{xpos(last_pos) - xpos(pos)}%"'
          s += '      onmouseover="'
          s += f"document.getElementById('_tp_{uuid}_ind_{ind}').style.textDecoration = 'underline';"
          s += f"document.getElementById('_fs_{uuid}_ind_{ind}').style.opacity = 1;"
          s += f"document.getElementById('_fb_{uuid}_ind_{ind}').style.opacity = 1;"
          s += '"'
          s += '      onmouseout="'
          s += f"document.getElementById('_tp_{uuid}_ind_{ind}').style.textDecoration = 'none';"
          s += f"document.getElementById('_fs_{uuid}_ind_{ind}').style.opacity = 0;"
          s += f"document.getElementById('_fb_{uuid}_ind_{ind}').style.opacity = 0;"
          s += '" style="fill:rgb(0,0,0,0)" />'
  
          last_pos = pos
  
      ### Negative value marks ###
  
      blue = tuple(colors.blue_rgb * 255)
      light_blue = (208, 230, 250)
  
      # draw base blue bar
      w = 100 * -values[values < 0].sum() / (xmax - xmin + 1e-8)
      s += f'<rect x="{xpos(fx)}%" width="{w}%" y="40" height="18" style="fill:rgb{blue}; stroke-width:0; stroke:rgb(0,0,0)" />'
  
      # draw underline marks and the text labels
      pos = fx
      last_pos = pos
      inds = [i for i in np.argsort(-np.abs(values)) if values[i] < 0]
      for i, ind in enumerate(inds):
          v = values[ind]
          pos -= v
  
          # a line under the bar to animate
          s += f'<line x1="{xpos(last_pos)}%" x2="{xpos(pos)}%" y1="60" y2="60" id="_fb_{uuid}_ind_{ind}" style="stroke:rgb{blue};stroke-width:2; opacity: 0"/>'
  
          # the value text
          s += f'<text x="{(xpos(last_pos) + xpos(pos))/2}%" y="71" font-size="12px" fill="rgb{blue}" id="_fs_{uuid}_ind_{ind}" style="opacity: 0" dominant-baseline="middle" text-anchor="middle">{values[ind].round(3)}</text>'
  
          # the text label cropped and centered
          s += f'<svg x="{xpos(last_pos)}%" y="40" height="20" width="{xpos(pos) - xpos(last_pos)}%">'
          s += '  <svg x="0" y="0" width="100%" height="100%">'
          s += f'    <text x="50%" y="9" font-size="12px" fill="rgb(255,255,255)" dominant-baseline="middle" text-anchor="middle">{tokens[ind].strip()}</text>'
          s += "  </svg>"
          s += "</svg>"
  
          last_pos = pos
  
      # draw the divider padding (which covers the text near the dividers)
      pos = fx
      for i, ind in enumerate(inds):
          v = values[ind]
          pos -= v
  
          if i != 0:
              for j in range(4):
                  s += f'<g transform="translate({-2*j+2},0)">'
                  s += f'  <svg x="{xpos(last_pos)}%" y="40" height="18" overflow="visible" width="30">'
                  s += f'    <path d="M 8 -9 l -6 18 L 8 25" fill="none" style="stroke:rgb{blue};stroke-width:2" />'
                  s += "  </svg>"
                  s += "</g>"
  
          if i + 1 != len(inds):
              for j in range(4):
                  s += f'<g transform="translate(-{2*j+8},0)">'
                  s += f'  <svg x="{xpos(pos)}%" y="40" height="18" overflow="visible" width="30">'
                  s += f'    <path d="M 8 -9 l -6 18 L 8 25" fill="none" style="stroke:rgb{blue};stroke-width:2" />'
                  s += "  </svg>"
                  s += "</g>"
  
          last_pos = pos
  
      # center padding
      s += f'<rect transform="translate(0,0)" x="{xpos(fx)}%" y="40" width="8" height="18" style="fill:rgb{blue}"/>'
  
      # cover up a notch at the end of the blue bar
      pos = fx - values[values < 0].sum()
      s += '<g transform="translate(-6.0,0)">'
      s += f'  <svg x="{xpos(pos)}%" y="40" height="18" overflow="visible" width="30">'
      s += '    <path d="M 8 -9 l -6 18 L 8 25 L 20 25 L 20 -9" fill="#ffffff" style="stroke:rgb(255,255,255);stroke-width:2" />'
      s += "  </svg>"
      s += "</g>"
  
      # draw the light blue divider lines and a rect to handle mouseover events
      pos = fx
      last_pos = pos
      for i, ind in enumerate(inds):
          v = values[ind]
          pos -= v
  
          # divider line
          if i + 1 != len(inds):
              s += '<g transform="translate(-6.0,0)">'
              s += f'  <svg x="{xpos(pos)}%" y="40" height="18" overflow="visible" width="30">'
              s += f'    <path d="M 8 -9 l -6 18 L 8 25" fill="none" style="stroke:rgb{light_blue};stroke-width:2" />'
              s += "  </svg>"
              s += "</g>"
  
          # mouse over rectangle
          s += f'<rect x="{xpos(last_pos)}%" y="40" height="20" width="{xpos(pos) - xpos(last_pos)}%"'
          s += '      onmouseover="'
          s += f"document.getElementById('_tp_{uuid}_ind_{ind}').style.textDecoration = 'underline';"
          s += f"document.getElementById('_fs_{uuid}_ind_{ind}').style.opacity = 1;"
          s += f"document.getElementById('_fb_{uuid}_ind_{ind}').style.opacity = 1;"
          s += '"'
          s += '      onmouseout="'
          s += f"document.getElementById('_tp_{uuid}_ind_{ind}').style.textDecoration = 'none';"
          s += f"document.getElementById('_fs_{uuid}_ind_{ind}').style.opacity = 0;"
          s += f"document.getElementById('_fb_{uuid}_ind_{ind}').style.opacity = 0;"
          s += '" style="fill:rgb(0,0,0,0)" />'
  
          last_pos = pos
  
      s += "</svg>"
  
      return s
  
file_path: shap/shap/plots/_text.py
imports: import json
  import random
  import string
  import numpy
  from .None import colors
label_desc: Use image node_id 3 for calling a global function with example usage: svg_force_plot(values, base_values, fx, tokens, uuid, xmin, xmax, output_name) and returns: s, unknown, s