Home » Football » Wuppertal vs Fortuna Dusseldorf II

Wuppertal vs Fortuna Dusseldorf II

Expert Analysis: Wuppertal vs Fortuna Dusseldorf II

The upcoming match between Wuppertal and Fortuna Dusseldorf II is set to be an exciting encounter with numerous betting opportunities. With a high average total goals of 4.71, this game promises plenty of action and potential for multiple scoring events. Both teams have shown tendencies to score, with Wuppertal averaging 1.38 goals per game and conceding 2.62, indicating a dynamic offensive and defensive play style.

Betting Predictions

Goal Oriented Bets

  • Over 1.5 Goals: 96.00 – Given the high average total goals, betting on more than 1.5 goals seems highly favorable.
  • Over 0.5 Goals HT: 79.80 – The likelihood of at least one goal in the first half is strong.
  • Both Teams To Score: 69.40 – Both teams have demonstrated their ability to find the back of the net, making this a compelling bet.
  • Over 2.5 Goals: 71.50 – With an average of nearly five goals per match, over 2.5 goals is a plausible outcome.
  • Goal In Last 15 Minutes: 80.30 – Late goals are common in this matchup, suggesting a good chance for a final-minute strike.
  • Last Goal After 73 Minutes: 86.80 – The trend indicates that late goals are likely, making this bet attractive.
  • Away Team To Score In 1st Half: 71.40Fortuna Dusseldorf II has the potential to score early in the game.
  • Away Team Not To Score In 2nd Half: 56.10**
  • Home Team To Score In 2nd Half: 59.90**

Timing Bets

  • First Goal Between Minute 0-29: 56.00**
  • Goal In Last 10 Minutes: 68.30**

No Goals Bets (Halves)

  • Home Team Not To Score In First Half: 87.80**
  • Away Team Not To Score In First Half: **71.40** (Complementary Bet)
  • Both Teams Not To Score In First Half: **64.60** (Complementary Bet)
  • Away Team Not To Score In Second Half: **56.10** (Complementary Bet)</luser

    I’m having trouble using `np.linalg.eigvals` with complex-valued matrices that have very small imaginary parts compared to their real parts:

    a = np.array([[0., .00001], [100., .00001]])
    b = np.array([[0., .000001], [100., .000001]])
    c = np.array([[0., .000001j], [100., .000001j]])
    
    eig_a = np.linalg.eigvals(a)
    eig_b = np.linalg.eigvals(b)
    eig_c = np.linalg.eigvals(c)
    
    print(eig_a)
    print(eig_b)
    print(eig_c)
    print(np.allclose(eig_a, eig_b))
    print(np.allclose(eig_a, eig_c))
    print(np.allclose(eig_b, eig_c))
    print(np.iscomplexobj(eig_a))
    print(np.iscomplexobj(eig_b))
    print(np.iscomplexobj(eig_c))
    # Output:
    # [100.+0.j         -0.-0.j]
    # [100.+0.j         -0.-0.j]
    # [100.+9.e-07j     -9.e-07j]
    # True
    # False
    # False
    # False
    # False
    # True
    

    The issue here is that `np.linalg.eigvals` returns complex values even when they should be real-valued; I'm not sure why it's doing this or how to get it to return real values when they're appropriate (i.e., when the imaginary part is very small).

    I've tried using `np.real_if_close`, but it doesn't seem to work for eigenvalues:

    eigenvalues_real_if_close = np.real_if_close(eig_a)
    eigenvalues_real_if_close_complex = np.real_if_close(eig_c)

    print(type(eigenvalues_real_if_close[0]))
    print(type(eigenvalues_real_if_close_complex[0]))
    # Output:
    # <class 'numpy.complexfloating'>
    # <class 'numpy.complexfloating'>
    print(np.allclose(a,eigenvalues_real_if_close_complex))
    # Output:
    # False

    for i in range(len(a)):
    for j in range(len(a)):
    if abs((a[i,j]-c[i,j])/a[i,j]) >= tolerance:
    return False
    return True

    def close_matrices(m,n,tolerance):
    return close_matrices(a,c,.00001)

    close_matrices(a,c,.00001) # This returns True as expected.
    close_matrices(b,c,.00001) # This returns False as expected.
    close_matrices(a,b,.00001) # This returns True as expected.
    close_matrices(b,a,.00001) # This returns True as expected.
    close_matrices(c,a,.00001) # This returns False as expected.
    close_matrices(c,b,.00001) # This returns False as expected.

    I would expect `np.real_if_close` to return real-valued arrays when appropriate because I'm only interested in the real part of these eigenvalues for my application.

    I also tried using `np.isreal` on each element in `eigenvalues_real_if_close`, but this still resulted in a complex array:

    eigenvalues_real_check = []

    for i in range(len(a)):
    for j in range(len(a)):
    if np.isreal(eigenvalues_real_if_close[i]):
    eigenvalues_real_check.append(np.real(eigenvalues_real_if_close[i]))
    else:
    eigenvalues_real_check.append(eigenvalues_real_if_close[i])

    print(type(eigenvalues_real_check[0]))
    #print([type(x) for x in eigenvalues_real_check])
    #print([x.imag for x in eigenvalues_real_check])
    #print([x.real for x in eigenvalues_real_check])
    Output:

    This is also surprising because `np.real` works fine on its own if you just want to extract the real part:

    eigenvalue_1_realsplit = []
    eigenvalue_2_realsplit = []

    for i,j in zip(*np.split(np.real(eigs),2)):
    eigenvalue_1_realsplit.append(i)
    eigenvalue_2_realsplit.append(j)

    print(type(eigenvalue_1_realsplit[0]))
    #print([type(x) for x in eigenvalue_1_realsplit])
    #print([x.imag for x in eigenvalue_1_realsplit])
    #print([x.real for x in eigenvalue_1_realsplit])

    Output:

    I think I'm missing something about how numpy treats complex numbers and floating-point precision here... Any suggestions?

    Edit:

    • If I use `np.round()` instead of `np.real()` on my original output from `np.linalg.eighvals`, then I get what I want:
      eigs_rounded_split = []
      for i,j in zip(*np.split(np.round(10000*eigs)/10000,2)):
      eigs_rounded_split.append(i)
      eigs_rounded_split.append(j)

      round_eigs_split_are_the_same_as_origs_split =
      all(
      [
      round_eigs_split_are_the_same_as_origs_split,
      all(abs(orig-eval)<=tolerance*abs(orig) for orig,eval
      in zip(origs_split,eigs_rounded_split)),
      ]
      )

      round_eigs_split_are_the_same_as_origs_split == True

      origs_rounded_split_are_the_same_as_round_eigs_split =
      all(
      [
      origs_rounded_split_are_the_same_as_round_eigs_split,
      all(abs(orig-eval)<=tolerance*abs(orig)
      for orig,eval
      in zip(origs_rounded_split,eigs_rounded_split)),
      ]
      )

      origs_rounded_split_are_the_same_as_round_eigs_split == True

      origs_rounded_and_origs_are_the_same =
      all(
      [
      origs_rounded_and_origs_are_the_same,
      all(abs(orig-eval)<=tolerance*abs(eval)
      for orig,eval
      in zip(origs_rounded,eorigs)),
      ]
      )

      origs_rounded_and_origs_are_the_same == True

      origs_all_about_equal =
      all([
      round_eigs_splits_are_the_same_as_orig_splits,
      origs_rondeds_splits_are_the_same_as_rondsplits,
      origroundedandorignarethesame,
      ])

      origes_all_about_equal == True

      This seems like kind of a hacky way of getting what I want though...

    Edit #2:

    • I don't know why my original code didn't work:
      eisrealchecksplit =
      [
      type(x)==float if eisrealchecksplit else type(x)==complex
      for x
      in eisrealchecksplit]

      The above gives me all floats like I wanted.

      I was hoping there was some better way though...